/* PowerPC64-specific support for 64-bit ELF.
- Copyright (C) 1999-2015 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_dtrel_excludes_plt 1
#define elf_backend_default_execstack 0
#define bfd_elf64_mkobject ppc64_elf_mkobject
#define bfd_elf64_bfd_link_hash_table_create ppc64_elf_link_hash_table_create
#define bfd_elf64_get_synthetic_symtab ppc64_elf_get_synthetic_symtab
#define bfd_elf64_bfd_link_just_syms ppc64_elf_link_just_syms
+#define bfd_elf64_bfd_gc_sections ppc64_elf_gc_sections
#define elf_backend_object_p ppc64_elf_object_p
#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
#define elf_backend_link_output_symbol_hook ppc64_elf_output_symbol_hook
#define elf_backend_special_sections ppc64_elf_special_sections
#define elf_backend_merge_symbol_attribute ppc64_elf_merge_symbol_attribute
+#define elf_backend_merge_symbol ppc64_elf_merge_symbol
+#define elf_backend_get_reloc_section bfd_get_section_by_name
/* The name of the dynamic interpreter. This is put in the .interp
section. */
#define LD_R2_0R2 0xe8420000 /* ld %r2,xxx+0(%r2) */
#define LD_R2_0R1 0xe8410000 /* ld %r2,0(%r1) */
+#define LD_R2_0R12 0xe84c0000 /* ld %r2,0(%r12) */
+#define ADD_R2_R2_R12 0x7c426214 /* add %r2,%r2,%r12 */
-#define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */
+#define LIS_R2 0x3c400000 /* lis %r2,xxx@ha */
+#define ADDIS_R2_R12 0x3c4c0000 /* addis %r2,%r12,xxx@ha */
+#define ADDIS_R12_R2 0x3d820000 /* addis %r12,%r2,xxx@ha */
#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,xxx@ha */
#define LD_R12_0R12 0xe98c0000 /* ld %r12,xxx@l(%r12) */
TRUE, /* pc_relative */
0, /* bitpos */
complain_overflow_signed, /* complain_on_overflow */
- bfd_elf_generic_reloc, /* special_function */
+ ppc64_elf_unhandled_reloc, /* special_function */
"R_PPC64_PLTREL32", /* name */
FALSE, /* partial_inplace */
0, /* src_mask */
0xffff, /* dst_mask */
TRUE), /* pcrel_offset */
+ /* Like R_PPC64_REL16_HA but for split field in addpcis. */
+ HOWTO (R_PPC64_REL16DX_HA, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ ppc64_elf_ha_reloc, /* special_function */
+ "R_PPC64_REL16DX_HA", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x1fffc1, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* A split-field reloc for addpcis, non-relative (gas internal use only). */
+ HOWTO (R_PPC64_16DX_HA, /* type */
+ 16, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_signed, /* complain_on_overflow */
+ ppc64_elf_ha_reloc, /* special_function */
+ "R_PPC64_16DX_HA", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0x1fffc1, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
/* Like R_PPC64_ADDR16_HI, but no overflow. */
HOWTO (R_PPC64_ADDR16_HIGH, /* type */
16, /* rightshift */
0xffff, /* dst_mask */
FALSE), /* pcrel_offset */
+ /* Marker reloc on ELFv2 large-model function entry. */
+ HOWTO (R_PPC64_ENTRY,
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_ENTRY", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
/* Like ADDR64, but use local entry point of function. */
HOWTO (R_PPC64_ADDR64_LOCAL, /* type */
0, /* rightshift */
break;
case BFD_RELOC_HI16_S_PCREL: r = R_PPC64_REL16_HA;
break;
+ case BFD_RELOC_PPC_16DX_HA: r = R_PPC64_16DX_HA;
+ break;
+ case BFD_RELOC_PPC_REL16DX_HA: r = R_PPC64_REL16DX_HA;
+ break;
+ case BFD_RELOC_PPC64_ENTRY: r = R_PPC64_ENTRY;
+ break;
case BFD_RELOC_PPC64_ADDR64_LOCAL: r = R_PPC64_ADDR64_LOCAL;
break;
case BFD_RELOC_VTABLE_INHERIT: r = R_PPC64_GNU_VTINHERIT;
/* Set the howto pointer for a PowerPC ELF reloc. */
static void
-ppc64_elf_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr,
+ppc64_elf_info_to_howto (bfd *abfd, arelent *cache_ptr,
Elf_Internal_Rela *dst)
{
unsigned int type;
type = ELF64_R_TYPE (dst->r_info);
if (type >= ARRAY_SIZE (ppc64_elf_howto_table))
{
- (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
- abfd, (int) type);
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%B: invalid relocation type %d"),
+ abfd, (int) type);
type = R_PPC64_NONE;
}
cache_ptr->howto = ppc64_elf_howto_table[type];
void *data, asection *input_section,
bfd *output_bfd, char **error_message)
{
+ enum elf_ppc64_reloc_type r_type;
+ long insn;
+ bfd_size_type octets;
+ bfd_vma value;
+
/* If this is a relocatable link (output_bfd test tells us), just
call the generic function. Any adjustment will be done at final
link time. */
We won't actually be using the low 16 bits, so trashing them
doesn't matter. */
reloc_entry->addend += 0x8000;
- return bfd_reloc_continue;
+ r_type = reloc_entry->howto->type;
+ if (r_type != R_PPC64_REL16DX_HA)
+ return bfd_reloc_continue;
+
+ value = 0;
+ if (!bfd_is_com_section (symbol->section))
+ value = symbol->value;
+ value += (reloc_entry->addend
+ + symbol->section->output_offset
+ + symbol->section->output_section->vma);
+ value -= (reloc_entry->address
+ + input_section->output_offset
+ + input_section->output_section->vma);
+ value = (bfd_signed_vma) value >> 16;
+
+ octets = reloc_entry->address * bfd_octets_per_byte (abfd);
+ insn = bfd_get_32 (abfd, (bfd_byte *) data + octets);
+ insn &= ~0x1fffc1;
+ insn |= (value & 0xffc1) | ((value & 0x3e) << 15);
+ bfd_put_32 (abfd, insn, (bfd_byte *) data + octets);
+ if (value + 0x8000 > 0xffff)
+ return bfd_reloc_overflow;
+ return bfd_reloc_ok;
}
static bfd_reloc_status_type
elf_symbol_type *elfsym = (elf_symbol_type *) symbol;
if (symbol->section->owner != abfd
+ && symbol->section->owner != NULL
&& abiversion (symbol->section->owner) >= 2)
{
unsigned int i;
}
/* Fix bad default arch selected for a 64 bit input bfd when the
- default is 32 bit. */
+ default is 32 bit. Also select arch based on apuinfo. */
static bfd_boolean
ppc64_elf_object_p (bfd *abfd)
{
- if (abfd->arch_info->the_default && abfd->arch_info->bits_per_word == 32)
+ if (!abfd->arch_info->the_default)
+ return TRUE;
+
+ if (abfd->arch_info->bits_per_word == 32)
{
Elf_Internal_Ehdr *i_ehdr = elf_elfheader (abfd);
BFD_ASSERT (abfd->arch_info->bits_per_word == 64);
}
}
- return TRUE;
+ return _bfd_elf_ppc_set_arch (abfd);
}
/* Support for core dump NOTE sections. */
\f
/* Parameters for the qsort hook. */
static bfd_boolean synthetic_relocatable;
+static asection *synthetic_opd;
/* qsort comparison function for ppc64_elf_get_synthetic_symtab. */
return 1;
/* then .opd symbols. */
- if (strcmp (a->section->name, ".opd") == 0
- && strcmp (b->section->name, ".opd") != 0)
- return -1;
- if (strcmp (a->section->name, ".opd") != 0
- && strcmp (b->section->name, ".opd") == 0)
- return 1;
+ if (synthetic_opd != NULL)
+ {
+ if (strcmp (a->section->name, ".opd") == 0
+ && strcmp (b->section->name, ".opd") != 0)
+ return -1;
+ if (strcmp (a->section->name, ".opd") != 0
+ && strcmp (b->section->name, ".opd") == 0)
+ return 1;
+ }
/* then other code symbols. */
if ((a->section->flags & (SEC_CODE | SEC_ALLOC | SEC_THREAD_LOCAL))
if ((a->flags & BSF_DYNAMIC) == 0 && (b->flags & BSF_DYNAMIC) != 0)
return 1;
- return 0;
+ return a > b;
}
/* Search SYMS for a symbol of the given VALUE. */
memcpy (syms, static_syms, (symcount + 1) * sizeof (*syms));
synthetic_relocatable = relocatable;
+ synthetic_opd = opd;
qsort (syms, symcount, sizeof (*syms), compare_symbols);
if (!relocatable && symcount > 1)
}
i = 0;
- if (strcmp (syms[i]->section->name, ".opd") == 0)
+ /* Note that here and in compare_symbols we can't compare opd and
+ sym->section directly. With separate debug info files, the
+ symbols will be extracted from the debug file while abfd passed
+ to this function is the real binary. */
+ if (opd != NULL && strcmp (syms[i]->section->name, ".opd") == 0)
++i;
codesecsym = i;
break;
secsymend = i;
- for (; i < symcount; ++i)
- if (strcmp (syms[i]->section->name, ".opd") != 0)
- break;
+ if (opd != NULL)
+ for (; i < symcount; ++i)
+ if (strcmp (syms[i]->section->name, ".opd") != 0)
+ break;
opdsymend = i;
for (; i < symcount; ++i)
calls may use the function descriptor symbol, ie. "bl foo". This
behaves exactly as "bl .foo". */
-/* 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. */
+/* Of those relocs that might be copied as dynamic relocs, this
+ function selects those that must be copied when linking a shared
+ library or PIE, even when the symbol is local. */
static int
must_be_dyn_reloc (struct bfd_link_info *info,
switch (r_type)
{
default:
+ /* Only relative relocs can be resolved when the object load
+ address isn't fixed. DTPREL64 is excluded because the
+ dynamic linker needs to differentiate global dynamic from
+ local dynamic __tls_index pairs when PPC64_OPT_TLS is set. */
return 1;
case R_PPC64_REL32:
case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_TPREL16_HIGHESTA:
case R_PPC64_TPREL64:
- return !bfd_link_executable (info);
+ /* These relocations are relative but in a shared library the
+ linker doesn't know the thread pointer base. */
+ return bfd_link_dll (info);
}
}
+/* Whether an undefined weak symbol should resolve to its link-time
+ value, even in PIC or PIE objects. */
+#define UNDEFWEAK_NO_DYNAMIC_RELOC(INFO, H) \
+ ((H)->root.type == bfd_link_hash_undefweak \
+ && (ELF_ST_VISIBILITY ((H)->other) != STV_DEFAULT \
+ || (INFO)->dynamic_undefined_weak == 0))
+
/* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
copying dynamic variables from a shared lib into an app's dynbss
section, and instead use a dynamic relocation to point into the
/* Whether to emit a copy of register save/restore functions in this
group. */
int needs_save_res;
+ /* The offset of the __tls_get_addr_opt plt stub bctrl in this group,
+ or -1u if no such stub with bctrl exists. */
+ unsigned int tls_get_addr_opt_bctrl;
};
struct ppc_stub_hash_entry {
/* Track dynamic relocs copied for this symbol. */
struct elf_dyn_relocs *dyn_relocs;
+ /* Chain of aliases referring to a weakdef. */
+ struct ppc_link_hash_entry *weakref;
+
/* Link between function code and descriptor symbols. */
struct ppc_link_hash_entry *oh;
should be set for all globals defined in any opd/toc section. */
unsigned int adjust_done:1;
- /* Set if we twiddled this symbol to weak at some stage. */
- unsigned int was_undefined:1;
-
/* Set if this is an out-of-line register save/restore function,
with non-standard calling convention. */
unsigned int save_res:1;
+ /* Set if a duplicate symbol with non-zero localentry is detected,
+ even when the duplicate symbol does not provide a definition. */
+ unsigned int non_zero_localentry:1;
+
/* Contexts in which symbol is used in the GOT (or TOC).
TLS_GD .. TLS_EXPLICIT bits are or'd into the mask as the
corresponding relocs are encountered during 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;
unsigned int second_toc_pass:1;
unsigned int do_toc_opt:1;
+ /* Set if tls optimization is enabled. */
+ unsigned int do_tls_opt:1;
+
/* Set on error. */
unsigned int stub_error:1;
- /* Temp used by ppc64_elf_before_check_relocs. */
- unsigned int twiddled_syms:1;
+ /* Whether func_desc_adjust needs to be run over symbols. */
+ unsigned int need_func_desc_adj:1;
+
+ /* Whether there exist local gnu indirect function resolvers,
+ referenced by dynamic relocations. */
+ unsigned int local_ifunc_resolver:1;
+ unsigned int maybe_local_ifunc_resolver:1;
+
+ /* Whether plt calls for ELFv2 localentry:0 funcs have been optimized. */
+ unsigned int has_plt_localentry0:1;
/* Incremented every time we size stubs. */
unsigned int stub_iteration;
tocsave_htab_hash (const void *p)
{
const struct tocsave_entry *e = (const struct tocsave_entry *) p;
- return ((bfd_vma)(intptr_t) e->sec ^ e->offset) >> 3;
+ return ((bfd_vma) (intptr_t) e->sec ^ e->offset) >> 3;
}
static int
htab = ppc_hash_table (info);
- /* Create .sfpr for code to save and restore fp regs. */
flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
| SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
- htab->sfpr = bfd_make_section_anyway_with_flags (dynobj, ".sfpr",
- flags);
- if (htab->sfpr == NULL
- || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
- return FALSE;
+ if (htab->params->save_restore_funcs)
+ {
+ /* Create .sfpr for code to save and restore fp regs. */
+ htab->sfpr = bfd_make_section_anyway_with_flags (dynobj, ".sfpr",
+ flags);
+ if (htab->sfpr == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
+ return FALSE;
+ }
+
+ if (bfd_link_relocatable (info))
+ return TRUE;
/* Create .glink for lazy dynamic linking support. */
htab->glink = bfd_make_section_anyway_with_flags (dynobj, ".glink",
linker created stub bfd. This ensures that the GOT header is at
the start of the output TOC section. */
htab = ppc_hash_table (info);
- if (htab == NULL)
- return FALSE;
htab->elf.dynobj = params->stub_bfd;
htab->params = params;
- if (bfd_link_relocatable (info))
- return TRUE;
-
return create_linkage_sections (htab->elf.dynobj, info);
}
more than one stub used to reach say, printf, and we need to
distinguish between them. */
group = htab->sec_info[input_section->id].u.group;
+ if (group == NULL)
+ return NULL;
if (h != NULL && h->u.stub_cache != NULL
&& h->u.stub_cache->h == h
TRUE, FALSE);
if (stub_entry == NULL)
{
+ /* xgettext:c-format */
info->callbacks->einfo (_("%P: %B: cannot create stub entry %s\n"),
section->owner, stub_name);
return NULL;
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 *
&& edir->elf.dynamic_adjusted))
edir->elf.non_got_ref |= eind->elf.non_got_ref;
- edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
+ 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
+ in order to simplify readonly_dynrelocs and save a field in the
+ symbol hash entry, but that means dyn_relocs can't be used in any
+ tests about a specific symbol, or affect other symbol flags which
+ are then tested.
+ Chain weakdefs so we can get from the weakdef back to an alias.
+ The list is circular so that we don't need to use u.weakdef as
+ well as this list to look at all aliases. */
+ if (eind->elf.root.type != bfd_link_hash_indirect)
+ {
+ struct ppc_link_hash_entry *cur, *add, *next;
+
+ add = eind;
+ do
+ {
+ cur = edir->weakref;
+ if (cur != NULL)
+ {
+ do
+ {
+ /* We can be called twice for the same symbols.
+ Don't make multiple loops. */
+ if (cur == add)
+ return;
+ cur = cur->weakref;
+ } while (cur != edir);
+ }
+ next = add->weakref;
+ if (cur != add)
+ {
+ add->weakref = edir->weakref != NULL ? edir->weakref : edir;
+ edir->weakref = add;
+ }
+ add = next;
+ } while (add != NULL && add != eind);
+ return;
+ }
+
/* Copy over any dynamic relocs we may have on the indirect sym. */
if (eind->dyn_relocs != NULL)
{
eind->dyn_relocs = NULL;
}
- /* If we were called to copy over info for a weak sym, that's all.
- You might think dyn_relocs need not be copied over; After all,
- both syms will be dynamic or both non-dynamic so we're just
- moving reloc accounting around. However, ELIMINATE_COPY_RELOCS
- code in ppc64_elf_adjust_dynamic_symbol needs to check for
- dyn_relocs in read-only sections, and it does so on what is the
- DIR sym here. */
- if (eind->elf.root.type != bfd_link_hash_indirect)
- return;
-
/* Copy over got entries that we may have already seen to the
symbol which just became indirect. */
if (eind->elf.got.glist != NULL)
fh->oh = fdh;
}
- return ppc_follow_link (fdh);
+ fdh = ppc_follow_link (fdh);
+ fdh->is_func_descriptor = 1;
+ fdh->oh = fh;
+ return fdh;
}
-/* Make a fake function descriptor sym for the code sym FH. */
+/* Make a fake function descriptor sym for the undefined code sym FH. */
static struct ppc_link_hash_entry *
make_fdh (struct bfd_link_info *info,
struct ppc_link_hash_entry *fh)
{
- bfd *abfd;
- asymbol *newsym;
- struct bfd_link_hash_entry *bh;
+ bfd *abfd = fh->elf.root.u.undef.abfd;
+ struct bfd_link_hash_entry *bh = NULL;
struct ppc_link_hash_entry *fdh;
-
- abfd = fh->elf.root.u.undef.abfd;
- newsym = bfd_make_empty_symbol (abfd);
- newsym->name = fh->elf.root.root.string + 1;
- newsym->section = bfd_und_section_ptr;
- newsym->value = 0;
- newsym->flags = BSF_WEAK;
-
- bh = NULL;
- if (!_bfd_generic_link_add_one_symbol (info, abfd, newsym->name,
- newsym->flags, newsym->section,
- newsym->value, NULL, FALSE, FALSE,
- &bh))
+ flagword flags = (fh->elf.root.type == bfd_link_hash_undefweak
+ ? BSF_WEAK
+ : BSF_GLOBAL);
+
+ if (!_bfd_generic_link_add_one_symbol (info, abfd,
+ fh->elf.root.root.string + 1,
+ flags, bfd_und_section_ptr, 0,
+ NULL, FALSE, FALSE, &bh))
return NULL;
fdh = (struct ppc_link_hash_entry *) bh;
asection **sec,
bfd_vma *value)
{
- if ((ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
- || ELF_ST_BIND (isym->st_info) == STB_GNU_UNIQUE)
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
&& (ibfd->flags & DYNAMIC) == 0
&& bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
- elf_tdata (info->output_bfd)->has_gnu_symbols = elf_gnu_symbol_any;
+ elf_tdata (info->output_bfd)->has_gnu_symbols |= elf_gnu_symbol_ifunc;
if (*sec != NULL
&& strcmp ((*sec)->name, ".opd") == 0)
bfd_boolean definition,
bfd_boolean dynamic)
{
- if (definition && !dynamic)
+ if (definition && (!dynamic || !h->def_regular))
h->other = ((isym->st_other & ~ELF_ST_VISIBILITY (-1))
| ELF_ST_VISIBILITY (h->other));
}
+/* Hook called on merging a symbol. We use this to clear "fake" since
+ we now have a real symbol. */
+
+static bfd_boolean
+ppc64_elf_merge_symbol (struct elf_link_hash_entry *h,
+ const Elf_Internal_Sym *isym,
+ asection **psec ATTRIBUTE_UNUSED,
+ bfd_boolean newdef ATTRIBUTE_UNUSED,
+ bfd_boolean olddef ATTRIBUTE_UNUSED,
+ bfd *oldbfd ATTRIBUTE_UNUSED,
+ const asection *oldsec ATTRIBUTE_UNUSED)
+{
+ ((struct ppc_link_hash_entry *) h)->fake = 0;
+ if ((STO_PPC64_LOCAL_MASK & isym->st_other) != 0)
+ ((struct ppc_link_hash_entry *) h)->non_zero_localentry = 1;
+ return TRUE;
+}
+
/* This function makes an old ABI object reference to ".bar" cause the
inclusion of a new ABI object archive that defines "bar".
NAME is a symbol defined in an archive. Return a symbol in the hash
if (h != NULL
/* Don't return this sym if it is a fake function descriptor
created by add_symbol_adjust. */
- && !(h->root.type == bfd_link_hash_undefweak
- && ((struct ppc_link_hash_entry *) h)->fake))
+ && !((struct ppc_link_hash_entry *) h)->fake)
return h;
if (name[0] == '.')
struct ppc_link_hash_table *htab;
struct ppc_link_hash_entry *fdh;
- if (eh->elf.root.type == bfd_link_hash_indirect)
- return TRUE;
-
if (eh->elf.root.type == bfd_link_hash_warning)
eh = (struct ppc_link_hash_entry *) eh->elf.root.u.i.link;
+ if (eh->elf.root.type == bfd_link_hash_indirect)
+ return TRUE;
+
if (eh->elf.root.root.string[0] != '.')
abort ();
return FALSE;
fdh = lookup_fdh (eh, htab);
- if (fdh == NULL)
- {
- if (!bfd_link_relocatable (info)
- && (eh->elf.root.type == bfd_link_hash_undefined
- || eh->elf.root.type == bfd_link_hash_undefweak)
- && eh->elf.ref_regular)
- {
- /* Make an undefweak function descriptor sym, which is enough to
- pull in an --as-needed shared lib, but won't cause link
- errors. Archives are handled elsewhere. */
- fdh = make_fdh (info, eh);
- if (fdh == NULL)
- return FALSE;
- fdh->elf.ref_regular = 1;
- }
+ if (fdh == NULL
+ && !bfd_link_relocatable (info)
+ && (eh->elf.root.type == bfd_link_hash_undefined
+ || eh->elf.root.type == bfd_link_hash_undefweak)
+ && eh->elf.ref_regular)
+ {
+ /* Make an undefined function descriptor sym, in order to
+ pull in an --as-needed shared lib. Archives are handled
+ elsewhere. */
+ fdh = make_fdh (info, eh);
+ if (fdh == NULL)
+ return FALSE;
}
- else
+
+ if (fdh != NULL)
{
unsigned entry_vis = ELF_ST_VISIBILITY (eh->elf.other) - 1;
unsigned descr_vis = ELF_ST_VISIBILITY (fdh->elf.other) - 1;
+
+ /* Make both descriptor and entry symbol have the most
+ constraining visibility of either symbol. */
if (entry_vis < descr_vis)
fdh->elf.other += entry_vis - descr_vis;
else if (entry_vis > descr_vis)
eh->elf.other += descr_vis - entry_vis;
- if ((fdh->elf.root.type == bfd_link_hash_defined
- || fdh->elf.root.type == bfd_link_hash_defweak)
- && eh->elf.root.type == bfd_link_hash_undefined)
- {
- eh->elf.root.type = bfd_link_hash_undefweak;
- eh->was_undefined = 1;
- htab->twiddled_syms = 1;
+ /* Propagate reference flags from entry symbol to function
+ descriptor symbol. */
+ fdh->elf.root.non_ir_ref_regular |= eh->elf.root.non_ir_ref_regular;
+ fdh->elf.root.non_ir_ref_dynamic |= eh->elf.root.non_ir_ref_dynamic;
+ fdh->elf.ref_regular |= eh->elf.ref_regular;
+ fdh->elf.ref_regular_nonweak |= eh->elf.ref_regular_nonweak;
+
+ if (!fdh->elf.forced_local
+ && fdh->elf.dynindx == -1
+ && fdh->elf.versioned != versioned_hidden
+ && (bfd_link_dll (info)
+ || fdh->elf.def_dynamic
+ || fdh->elf.ref_dynamic)
+ && (eh->elf.ref_regular
+ || eh->elf.def_regular))
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, &fdh->elf))
+ return FALSE;
}
}
{
if (abiversion (ibfd) == 0)
set_abiversion (ibfd, 1);
- else if (abiversion (ibfd) == 2)
+ else if (abiversion (ibfd) >= 2)
{
+ /* xgettext:c-format */
info->callbacks->einfo (_("%P: %B .opd not allowed in ABI"
" version %d\n"),
ibfd, abiversion (ibfd));
else if (htab->elf.hgot == NULL
&& strcmp (eh->elf.root.root.string, ".TOC.") == 0)
htab->elf.hgot = &eh->elf;
- else if (!add_symbol_adjust (eh, info))
- return FALSE;
- p = &eh->u.next_dot_sym;
- }
-
- /* Clear the list for non-ppc64 input files. */
- p = &htab->dot_syms;
- while ((eh = *p) != NULL)
- {
- *p = NULL;
+ else if (abiversion (ibfd) <= 1)
+ {
+ htab->need_func_desc_adj = 1;
+ if (!add_symbol_adjust (eh, info))
+ return FALSE;
+ }
p = &eh->u.next_dot_sym;
}
-
- /* We need to fix the undefs list for any syms we have twiddled to
- undef_weak. */
- if (htab->twiddled_syms)
- {
- bfd_link_repair_undef_list (&htab->elf.root);
- htab->twiddled_syms = 0;
- }
return TRUE;
}
enum elf_ppc64_reloc_type r_type;
int tls_type;
struct _ppc64_elf_section_data *ppc64_sec;
- struct plt_entry **ifunc;
+ struct plt_entry **ifunc, **plt_list;
r_symndx = ELF64_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
h = NULL;
else
{
+ struct ppc_link_hash_entry *eh;
+
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
h = elf_follow_link (h);
+ eh = (struct ppc_link_hash_entry *) h;
/* PR15323, ref flags aren't set for references in the same
object. */
- h->root.non_ir_ref = 1;
+ h->root.non_ir_ref_regular = 1;
+ if (eh->is_func && eh->oh != NULL)
+ eh->oh->elf.root.non_ir_ref_regular = 1;
if (h == htab->elf.hgot)
sec->has_toc_reloc = 1;
return FALSE;
}
}
- r_type = ELF64_R_TYPE (rel->r_info);
- if (is_branch_reloc (r_type))
- {
- if (h != NULL && (h == tga || h == dottga))
- {
- if (rel != relocs
- && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
- || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
- /* We have a new-style __tls_get_addr call with a marker
- reloc. */
- ;
- else
- /* Mark this section as having an old-style call. */
- sec->has_tls_get_addr_call = 1;
- }
-
- /* STT_GNU_IFUNC symbols must have a PLT entry. */
- if (ifunc != NULL
- && !update_plt_info (abfd, ifunc, rel->r_addend))
- return FALSE;
- }
+ r_type = ELF64_R_TYPE (rel->r_info);
switch (r_type)
{
case R_PPC64_TLSGD:
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_TPREL16_HA:
- if (bfd_link_pic (info))
+ if (bfd_link_dll (info))
info->flags |= DF_STATIC_TLS;
tls_type = TLS_TLS | TLS_TPREL;
goto dogottls;
tls_type = TLS_TLS | TLS_DTPREL;
dogottls:
sec->has_tls_reloc = 1;
- /* Fall thru */
+ /* Fall through */
case R_PPC64_GOT16:
case R_PPC64_GOT16_DS:
case R_PPC64_PLT16_LO:
case R_PPC64_PLT32:
case R_PPC64_PLT64:
- /* This symbol requires a procedure linkage table entry. We
- actually build the entry in adjust_dynamic_symbol,
- because this might be a case of linking PIC code without
- linking in any dynamic objects, in which case we don't
- need to generate a procedure linkage table after all. */
- if (h == NULL)
- {
- /* It does not make sense to have a procedure linkage
- table entry for a local symbol. */
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
- }
- else
+ /* This symbol requires a procedure linkage table entry. */
+ plt_list = ifunc;
+ if (h != NULL)
{
- if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
- return FALSE;
h->needs_plt = 1;
if (h->root.root.string[0] == '.'
&& h->root.root.string[1] != '\0')
((struct ppc_link_hash_entry *) h)->is_func = 1;
+ plt_list = &h->plt.plist;
+ }
+ if (plt_list == NULL)
+ {
+ /* It does not make sense to have a procedure linkage
+ table entry for a non-ifunc local symbol. */
+ info->callbacks->einfo
+ /* xgettext:c-format */
+ (_("%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);
+ return FALSE;
}
+ if (!update_plt_info (abfd, plt_list, rel->r_addend))
+ return FALSE;
break;
/* The following relocations don't need to propagate the
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HI:
case R_PPC64_REL16_HA:
+ case R_PPC64_REL16DX_HA:
break;
/* Not supported as a dynamic relocation. */
{
if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
ppc_howto_init ();
- info->callbacks->einfo (_("%P: %H: %s reloc unsupported "
+ /* xgettext:c-format */
+ 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);
case R_PPC64_TOC16_DS:
htab->do_multi_toc = 1;
ppc64_elf_tdata (abfd)->has_small_toc_reloc = 1;
+ /* Fall through. */
case R_PPC64_TOC16_LO:
case R_PPC64_TOC16_HI:
case R_PPC64_TOC16_HA:
sec->has_toc_reloc = 1;
break;
+ /* Marker reloc. */
+ case R_PPC64_ENTRY:
+ break;
+
/* This relocation describes the C++ object vtable hierarchy.
Reconstruct it for later use during GC. */
case R_PPC64_GNU_VTINHERIT:
/* Fall through. */
case R_PPC64_REL24:
- if (h != NULL && ifunc == NULL)
+ plt_list = ifunc;
+ if (h != NULL)
{
- /* We may need a .plt entry if the function this reloc
- refers to is in a shared lib. */
- if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
- return FALSE;
h->needs_plt = 1;
if (h->root.root.string[0] == '.'
&& h->root.root.string[1] != '\0')
((struct ppc_link_hash_entry *) h)->is_func = 1;
+
if (h == tga || h == dottga)
- sec->has_tls_reloc = 1;
+ {
+ sec->has_tls_reloc = 1;
+ if (rel != relocs
+ && (ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSGD
+ || ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_TLSLD))
+ /* We have a new-style __tls_get_addr call with
+ a marker reloc. */
+ ;
+ else
+ /* Mark this section as having an old-style call. */
+ sec->has_tls_get_addr_call = 1;
+ }
+ plt_list = &h->plt.plist;
}
+
+ /* We may need a .plt entry if the function this reloc
+ refers to is in a shared lib. */
+ if (plt_list
+ && !update_plt_info (abfd, plt_list, rel->r_addend))
+ return FALSE;
break;
+ case R_PPC64_ADDR14:
+ case R_PPC64_ADDR14_BRNTAKEN:
+ case R_PPC64_ADDR14_BRTAKEN:
+ case R_PPC64_ADDR24:
+ goto dodyn;
+
case R_PPC64_TPREL64:
tls_type = TLS_EXPLICIT | TLS_TLS | TLS_TPREL;
- if (bfd_link_pic (info))
+ if (bfd_link_dll (info))
info->flags |= DF_STATIC_TLS;
goto dotlstoc;
case R_PPC64_TPREL16_HIGHERA:
case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_TPREL16_HIGHESTA:
- if (bfd_link_pic (info))
- {
- info->flags |= DF_STATIC_TLS;
- goto dodyn;
- }
- break;
+ if (bfd_link_dll (info))
+ info->flags |= DF_STATIC_TLS;
+ goto dodyn;
case R_PPC64_ADDR64:
if (opd_sym_map != NULL
&& ELF64_R_TYPE ((rel + 1)->r_info) == R_PPC64_TOC)
{
if (h != NULL)
- {
- if (h->root.root.string[0] == '.'
- && h->root.root.string[1] != 0
- && lookup_fdh ((struct ppc_link_hash_entry *) h, htab))
- ;
- else
- ((struct ppc_link_hash_entry *) h)->is_func = 1;
- }
+ ((struct ppc_link_hash_entry *) h)->is_func = 1;
else
{
asection *s;
case R_PPC64_REL30:
case R_PPC64_REL32:
case R_PPC64_REL64:
- case R_PPC64_ADDR14:
- case R_PPC64_ADDR14_BRNTAKEN:
- case R_PPC64_ADDR14_BRTAKEN:
- case R_PPC64_ADDR24:
case R_PPC64_ADDR32:
case R_PPC64_UADDR16:
case R_PPC64_UADDR32:
object file when linking. */
static bfd_boolean
-ppc64_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+ppc64_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
{
+ bfd *obfd = info->output_bfd;
unsigned long iflags, oflags;
if ((ibfd->flags & BFD_LINKER_CREATED) != 0)
if (!is_ppc64_elf (ibfd) || !is_ppc64_elf (obfd))
return TRUE;
- if (!_bfd_generic_verify_endian_match (ibfd, obfd))
+ if (!_bfd_generic_verify_endian_match (ibfd, info))
return FALSE;
iflags = elf_elfheader (ibfd)->e_flags;
if (iflags & ~EF_PPC64_ABI)
{
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B uses unknown e_flags 0x%lx"), ibfd, iflags);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
else if (iflags != oflags && iflags != 0)
{
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: ABI version %ld is not compatible with ABI version %ld output"),
ibfd, iflags, oflags);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
+ _bfd_elf_ppc_merge_fp_attributes (ibfd, info);
+
/* Merge Tag_compatibility attributes and any common GNU ones. */
- _bfd_elf_merge_object_attributes (ibfd, obfd);
+ _bfd_elf_merge_object_attributes (ibfd, info);
return TRUE;
}
{
FILE *file = ptr;
- /* xgettext:c-format */
fprintf (file, _("private flags = 0x%lx:"),
elf_elfheader (abfd)->e_flags);
return size;
}
+/* Return true if symbol is a strong function defined in an ELFv2
+ object with st_other localentry bits of zero, ie. its local entry
+ point coincides with its global entry point. */
+
+static bfd_boolean
+is_elfv2_localentry0 (struct elf_link_hash_entry *h)
+{
+ return (h != NULL
+ && h->type == STT_FUNC
+ && h->root.type == bfd_link_hash_defined
+ && (STO_PPC64_LOCAL_MASK & h->other) == 0
+ && !((struct ppc_link_hash_entry *) h)->non_zero_localentry
+ && is_ppc64_elf (h->root.u.def.section->owner)
+ && abiversion (h->root.u.def.section->owner) >= 2);
+}
+
/* Return true if symbol is defined in a regular object file. */
static bfd_boolean
return NULL;
}
+static bfd_boolean func_desc_adjust (struct elf_link_hash_entry *, void *);
+
+/* Garbage collect sections, after first dealing with dot-symbols. */
+
+static bfd_boolean
+ppc64_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
+{
+ struct ppc_link_hash_table *htab = ppc_hash_table (info);
+
+ if (htab != NULL && htab->need_func_desc_adj)
+ {
+ elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
+ htab->need_func_desc_adj = 0;
+ }
+ return bfd_elf_gc_sections (abfd, info);
+}
+
/* Mark all our entry sym sections, both opd and code section. */
static void
&& 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)))))
{
eh = (struct ppc_link_hash_entry *) h;
fdh = defined_func_desc (eh);
if (fdh != NULL)
- eh = fdh;
+ {
+ /* -mcall-aixdesc code references the dot-symbol on
+ a call reloc. Mark the function descriptor too
+ against garbage collection. */
+ fdh->elf.mark = 1;
+ if (fdh->elf.u.weakdef != NULL)
+ fdh->elf.u.weakdef->mark = 1;
+ eh = fdh;
+ }
/* Function descriptor syms cause the associated
function code sym section to be marked. */
unsigned long r_symndx;
enum elf_ppc64_reloc_type r_type;
struct elf_link_hash_entry *h = NULL;
+ struct plt_entry **plt_list = NULL;
unsigned char tls_type = 0;
r_symndx = ELF64_R_SYM (rel->r_info);
}
}
- if (is_branch_reloc (r_type))
- {
- struct plt_entry **ifunc = NULL;
- if (h != NULL)
- {
- if (h->type == STT_GNU_IFUNC)
- ifunc = &h->plt.plist;
- }
- else if (local_got_ents != NULL)
- {
- struct plt_entry **local_plt = (struct plt_entry **)
- (local_got_ents + symtab_hdr->sh_info);
- unsigned char *local_got_tls_masks = (unsigned char *)
- (local_plt + symtab_hdr->sh_info);
- if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
- ifunc = local_plt + r_symndx;
- }
- if (ifunc != NULL)
- {
- struct plt_entry *ent;
-
- for (ent = *ifunc; ent != NULL; ent = ent->next)
- if (ent->addend == rel->r_addend)
- break;
- if (ent == NULL)
- abort ();
- if (ent->plt.refcount > 0)
- ent->plt.refcount -= 1;
- continue;
- }
- }
-
- switch (r_type)
+ switch (r_type)
{
case R_PPC64_GOT_TLSLD16:
case R_PPC64_GOT_TLSLD16_LO:
if (ent->got.refcount > 0)
ent->got.refcount -= 1;
}
+ if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1)
+ plt_list = &h->plt.plist;
break;
case R_PPC64_PLT16_HA:
case R_PPC64_REL14_BRTAKEN:
case R_PPC64_REL24:
if (h != NULL)
+ plt_list = &h->plt.plist;
+ else if (local_got_ents != NULL)
{
- struct plt_entry *ent;
-
- for (ent = h->plt.plist; ent != NULL; ent = ent->next)
- if (ent->addend == rel->r_addend)
- break;
- if (ent != NULL && ent->plt.refcount > 0)
- ent->plt.refcount -= 1;
+ struct plt_entry **local_plt = (struct plt_entry **)
+ (local_got_ents + symtab_hdr->sh_info);
+ unsigned char *local_got_tls_masks = (unsigned char *)
+ (local_plt + symtab_hdr->sh_info);
+ if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
+ plt_list = local_plt + r_symndx;
}
break;
+ case R_PPC64_ADDR64:
+ case R_PPC64_ADDR16:
+ case R_PPC64_ADDR16_DS:
+ case R_PPC64_ADDR16_HA:
+ case R_PPC64_ADDR16_HI:
+ case R_PPC64_ADDR16_HIGH:
+ case R_PPC64_ADDR16_HIGHA:
+ case R_PPC64_ADDR16_HIGHER:
+ case R_PPC64_ADDR16_HIGHERA:
+ case R_PPC64_ADDR16_HIGHEST:
+ case R_PPC64_ADDR16_HIGHESTA:
+ case R_PPC64_ADDR16_LO:
+ case R_PPC64_ADDR16_LO_DS:
+ if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1
+ && rel->r_addend == 0)
+ plt_list = &h->plt.plist;
+ break;
+
default:
break;
}
+ if (plt_list != NULL)
+ {
+ struct plt_entry *ent;
+
+ for (ent = *plt_list; ent != NULL; ent = ent->next)
+ if (ent->addend == rel->r_addend)
+ break;
+ if (ent != NULL && ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
+ }
}
return TRUE;
}
sym[len + 0] = i / 10 + '0';
sym[len + 1] = i % 10 + '0';
h = (struct ppc_link_hash_entry *)
- elf_link_hash_lookup (&htab->elf, sym, FALSE, FALSE, TRUE);
+ elf_link_hash_lookup (&htab->elf, sym, writing, TRUE, TRUE);
if (stub_sec != NULL)
{
if (h != NULL
h->elf.root.u.def.value = htab->sfpr->size;
h->elf.type = STT_FUNC;
h->elf.def_regular = 1;
+ h->elf.non_elf = 0;
_bfd_elf_link_hash_hide_symbol (info, &h->elf, TRUE);
writing = TRUE;
if (htab->sfpr->contents == NULL)
{
struct bfd_link_info *info;
struct ppc_link_hash_table *htab;
- struct plt_entry *ent;
struct ppc_link_hash_entry *fh;
struct ppc_link_hash_entry *fdh;
bfd_boolean force_local;
if (fh->elf.root.type == bfd_link_hash_indirect)
return TRUE;
+ if (!fh->is_func)
+ return TRUE;
+
+ if (fh->elf.root.root.string[0] != '.'
+ || fh->elf.root.root.string[1] == '\0')
+ return TRUE;
+
info = inf;
htab = ppc_hash_table (info);
if (htab == NULL)
return FALSE;
+ /* Find the corresponding function descriptor symbol. */
+ fdh = lookup_fdh (fh, htab);
+
/* Resolve undefined references to dot-symbols as the value
in the function descriptor, if we have one in a regular object.
This is to satisfy cases like ".quad .foo". Calls to functions
in dynamic objects are handled elsewhere. */
- if (fh->elf.root.type == bfd_link_hash_undefweak
- && fh->was_undefined
- && (fdh = defined_func_desc (fh)) != NULL
+ if ((fh->elf.root.type == bfd_link_hash_undefined
+ || fh->elf.root.type == bfd_link_hash_undefweak)
+ && (fdh->elf.root.type == bfd_link_hash_defined
+ || fdh->elf.root.type == bfd_link_hash_defweak)
&& get_opd_info (fdh->elf.root.u.def.section) != NULL
&& opd_entry_value (fdh->elf.root.u.def.section,
fdh->elf.root.u.def.value,
fh->elf.def_dynamic = fdh->elf.def_dynamic;
}
- /* If this is a function code symbol, transfer dynamic linking
- information to the function descriptor symbol. */
- if (!fh->is_func)
- return TRUE;
-
- for (ent = fh->elf.plt.plist; ent != NULL; ent = ent->next)
- if (ent->plt.refcount > 0)
- break;
- if (ent == NULL
- || fh->elf.root.root.string[0] != '.'
- || fh->elf.root.root.string[1] == '\0')
- return TRUE;
+ if (!fh->elf.dynamic)
+ {
+ struct plt_entry *ent;
- /* Find the corresponding function descriptor symbol. Create it
- as undefined if necessary. */
+ for (ent = fh->elf.plt.plist; ent != NULL; ent = ent->next)
+ if (ent->plt.refcount > 0)
+ break;
+ if (ent == NULL)
+ return TRUE;
+ }
- fdh = lookup_fdh (fh, htab);
+ /* Create a descriptor as undefined if necessary. */
if (fdh == NULL
&& !bfd_link_executable (info)
&& (fh->elf.root.type == bfd_link_hash_undefined
return FALSE;
}
- /* Fake function descriptors are made undefweak. If the function
- code symbol is strong undefined, make the fake sym the same.
- If the function code symbol is defined, then force the fake
- descriptor local; We can't support overriding of symbols in a
- shared library on a fake descriptor. */
-
+ /* We can't support overriding of symbols on a fake descriptor. */
if (fdh != NULL
&& fdh->fake
- && fdh->elf.root.type == bfd_link_hash_undefweak)
- {
- if (fh->elf.root.type == bfd_link_hash_undefined)
- {
- fdh->elf.root.type = bfd_link_hash_undefined;
- bfd_link_add_undef (&htab->elf.root, &fdh->elf.root);
- }
- else if (fh->elf.root.type == bfd_link_hash_defined
- || fh->elf.root.type == bfd_link_hash_defweak)
- {
- _bfd_elf_link_hash_hide_symbol (info, &fdh->elf, TRUE);
- }
- }
+ && (fh->elf.root.type == bfd_link_hash_defined
+ || fh->elf.root.type == bfd_link_hash_defweak))
+ _bfd_elf_link_hash_hide_symbol (info, &fdh->elf, TRUE);
- if (fdh != NULL
- && !fdh->elf.forced_local
- && (!bfd_link_executable (info)
- || fdh->elf.def_dynamic
- || fdh->elf.ref_dynamic
- || (fdh->elf.root.type == bfd_link_hash_undefweak
- && ELF_ST_VISIBILITY (fdh->elf.other) == STV_DEFAULT)))
- {
- if (fdh->elf.dynindx == -1)
- if (! bfd_elf_link_record_dynamic_symbol (info, &fdh->elf))
- return FALSE;
+ /* Transfer dynamic linking information to the function descriptor. */
+ if (fdh != NULL)
+ {
fdh->elf.ref_regular |= fh->elf.ref_regular;
fdh->elf.ref_dynamic |= fh->elf.ref_dynamic;
fdh->elf.ref_regular_nonweak |= fh->elf.ref_regular_nonweak;
fdh->elf.non_got_ref |= fh->elf.non_got_ref;
- if (ELF_ST_VISIBILITY (fh->elf.other) == STV_DEFAULT)
- {
- move_plt_plist (fh, fdh);
- fdh->elf.needs_plt = 1;
- }
- fdh->is_func_descriptor = 1;
- fdh->oh = fh;
- fh->oh = fdh;
+ fdh->elf.dynamic |= fh->elf.dynamic;
+ fdh->elf.needs_plt |= (fh->elf.needs_plt
+ || fh->elf.type == STT_FUNC
+ || fh->elf.type == STT_GNU_IFUNC);
+ move_plt_plist (fh, fdh);
+
+ if (!fdh->elf.forced_local
+ && fh->elf.dynindx != -1)
+ if (!bfd_elf_link_record_dynamic_symbol (info, &fdh->elf))
+ return FALSE;
}
/* Now that the info is on the function descriptor, clear the
struct bfd_link_info *info)
{
struct ppc_link_hash_table *htab;
- unsigned int i;
htab = ppc_hash_table (info);
if (htab == NULL)
return FALSE;
- if (!bfd_link_relocatable (info)
- && htab->elf.hgot != NULL)
+ /* Provide any missing _save* and _rest* functions. */
+ if (htab->sfpr != NULL)
+ {
+ unsigned int i;
+
+ htab->sfpr->size = 0;
+ for (i = 0; i < ARRAY_SIZE (save_res_funcs); i++)
+ if (!sfpr_define (info, &save_res_funcs[i], NULL))
+ return FALSE;
+ if (htab->sfpr->size == 0)
+ htab->sfpr->flags |= SEC_EXCLUDE;
+ }
+
+ if (bfd_link_relocatable (info))
+ return TRUE;
+
+ if (htab->elf.hgot != NULL)
{
_bfd_elf_link_hash_hide_symbol (info, htab->elf.hgot, TRUE);
/* Make .TOC. defined so as to prevent it being made dynamic.
| STV_HIDDEN);
}
- if (htab->sfpr == NULL)
- /* We don't have any relocs. */
- return TRUE;
-
- /* Provide any missing _save* and _rest* functions. */
- htab->sfpr->size = 0;
- if (htab->params->save_restore_funcs)
- for (i = 0; i < ARRAY_SIZE (save_res_funcs); i++)
- if (!sfpr_define (info, &save_res_funcs[i], NULL))
- return FALSE;
-
- elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
-
- if (htab->sfpr->size == 0)
- htab->sfpr->flags |= SEC_EXCLUDE;
+ if (htab->need_func_desc_adj)
+ {
+ elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
+ htab->need_func_desc_adj = 0;
+ }
return TRUE;
}
-/* Return true if we have dynamic relocs that apply to read-only sections. */
+/* Return true if we have dynamic relocs against H that apply to
+ read-only sections. */
static bfd_boolean
readonly_dynrelocs (struct elf_link_hash_entry *h)
return FALSE;
}
+/* Return true if we have dynamic relocs against H or any of its weak
+ aliases, that apply to read-only sections. */
+
+static bfd_boolean
+alias_readonly_dynrelocs (struct elf_link_hash_entry *h)
+{
+ struct ppc_link_hash_entry *eh;
+
+ eh = (struct ppc_link_hash_entry *) h;
+ do
+ {
+ if (readonly_dynrelocs (&eh->elf))
+ return TRUE;
+ eh = eh->weakref;
+ } while (eh != NULL && &eh->elf != h);
+
+ return FALSE;
+}
+
+/* Return whether EH has pc-relative dynamic relocs. */
+
+static bfd_boolean
+pc_dynrelocs (struct ppc_link_hash_entry *eh)
+{
+ struct elf_dyn_relocs *p;
+
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ if (p->pc_count != 0)
+ return TRUE;
+ return FALSE;
+}
+
+/* Return true if a global entry stub will be created for H. Valid
+ for ELFv2 before plt entries have been allocated. */
+
+static bfd_boolean
+global_entry_stub (struct elf_link_hash_entry *h)
+{
+ struct plt_entry *pent;
+
+ if (!h->pointer_equality_needed
+ || h->def_regular)
+ return FALSE;
+
+ for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+ if (pent->plt.refcount > 0
+ && pent->addend == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
/* Adjust a symbol defined by a dynamic object and referenced by a
regular object. The current definition is in some section of the
dynamic object, but we're not including those sections. We have to
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)
if (ent == NULL
|| (h->type != STT_GNU_IFUNC
&& (SYMBOL_CALLS_LOCAL (info, h)
- || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
- && h->root.type == bfd_link_hash_undefweak)))
+ || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)))
|| ((struct ppc_link_hash_entry *) h)->save_res)
{
h->plt.plist = NULL;
h->needs_plt = 0;
h->pointer_equality_needed = 0;
}
- else if (abiversion (info->output_bfd) == 2)
+ else if (abiversion (info->output_bfd) >= 2)
{
/* Taking a function's address in a read/write section
doesn't require us to define the function symbol in the
executable on a global entry stub. A dynamic reloc can
- be used instead. */
- if (h->pointer_equality_needed
- && h->type != STT_GNU_IFUNC
- && !readonly_dynrelocs (h))
+ be used instead. The reason we prefer a few more dynamic
+ relocs is that calling via a global entry stub costs a
+ few more instructions, and pointer_equality_needed causes
+ extra work in ld.so when resolving these symbols. */
+ if (global_entry_stub (h)
+ && !alias_readonly_dynrelocs (h))
{
h->pointer_equality_needed = 0;
+ /* After adjust_dynamic_symbol, non_got_ref set in
+ the non-pic case means that dyn_relocs for this
+ symbol should be discarded. */
h->non_got_ref = 0;
}
- /* After adjust_dynamic_symbol, non_got_ref set in the
- non-shared case means that we have allocated space in
- .dynbss for the symbol and thus dyn_relocs for this
- symbol should be discarded.
- If we get here we know we are making a PLT entry for this
- symbol, and in an executable we'd normally resolve
- relocations against this symbol to the PLT entry. Allow
- dynamic relocs if the reference is weak, and the dynamic
- relocs will not cause text relocation. */
- else if (!h->ref_regular_nonweak
- && h->non_got_ref
- && h->type != STT_GNU_IFUNC
- && !readonly_dynrelocs (h))
- h->non_got_ref = 0;
-
/* If making a plt entry, then we don't need copy relocs. */
return TRUE;
}
return TRUE;
/* Don't generate a copy reloc for symbols defined in the executable. */
- if (!h->def_dynamic || !h->ref_regular || h->def_regular)
- return TRUE;
+ if (!h->def_dynamic || !h->ref_regular || h->def_regular
- /* If -z nocopyreloc was given, don't generate them either. */
- if (info->nocopyreloc)
- {
- h->non_got_ref = 0;
- return TRUE;
- }
+ /* If -z nocopyreloc was given, don't generate them either. */
+ || info->nocopyreloc
- /* If we didn't find any dynamic relocs in read-only sections, then
- we'll be keeping the dynamic relocs and avoiding the copy reloc. */
- if (ELIMINATE_COPY_RELOCS && !readonly_dynrelocs (h))
- {
- h->non_got_ref = 0;
- return TRUE;
- }
+ /* If we didn't find any dynamic relocs in read-only sections, then
+ we'll be keeping the dynamic relocs and avoiding the copy reloc. */
+ || (ELIMINATE_COPY_RELOCS && !alias_readonly_dynrelocs (h))
- /* Protected variables do not work with .dynbss. The copy in
- .dynbss won't be used by the shared library with the protected
- definition for the variable. Text relocations are preferable
- to an incorrect program. */
- if (h->protected_def)
+ /* Protected variables do not work with .dynbss. The copy in
+ .dynbss won't be used by the shared library with the protected
+ definition for the variable. Text relocations are preferable
+ to an incorrect program. */
+ || h->protected_def)
{
h->non_got_ref = 0;
return TRUE;
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);
}
if (fh == NULL)
{
const char *p, *q;
- struct ppc_link_hash_table *htab;
+ struct elf_link_hash_table *htab = elf_hash_table (info);
char save;
/* We aren't supposed to use alloca in BFD because on
p = eh->elf.root.root.string - 1;
save = *p;
*(char *) p = '.';
- htab = ppc_hash_table (info);
- if (htab == NULL)
- return;
-
fh = (struct ppc_link_hash_entry *)
- elf_link_hash_lookup (&htab->elf, p, FALSE, FALSE, FALSE);
+ elf_link_hash_lookup (htab, p, FALSE, FALSE, FALSE);
*(char *) p = save;
/* Unfortunately, if it so happens that the string we were
--q, --p;
if (q < eh->elf.root.root.string && *p == '.')
fh = (struct ppc_link_hash_entry *)
- elf_link_hash_lookup (&htab->elf, p, FALSE, FALSE, FALSE);
+ elf_link_hash_lookup (htab, p, FALSE, FALSE, FALSE);
}
if (fh != NULL)
{
return NULL;
if (ent.sec == NULL || ent.sec->output_section == NULL)
{
- (*_bfd_error_handler)
- (_("%B: undefined symbol on R_PPC64_TOCSAVE relocation"));
+ _bfd_error_handler
+ (_("%B: undefined symbol on R_PPC64_TOCSAVE relocation"), ibfd);
return NULL;
}
case R_PPC64_TPREL16_HIGHERA:
case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_TPREL16_HIGHESTA:
- if (!bfd_link_pic (info))
- return TRUE;
-
case R_PPC64_TPREL64:
case R_PPC64_DTPMOD64:
case R_PPC64_DTPREL64:
}
}
+ /* xgettext:c-format */
info->callbacks->einfo (_("%P: dynreloc miscount for %B, section %A\n"),
sec->owner, sec);
bfd_set_error (bfd_error_bad_value);
something silly in .opd with the assembler. No .opd
optimization for them! */
broken_opd:
- (*_bfd_error_handler)
+ _bfd_error_handler
(_("%B: .opd is not a regular array of opd entries"), ibfd);
broken = TRUE;
break;
if ((r_type = ELF64_R_TYPE (rel->r_info)) != R_PPC64_ADDR64
|| (r_type = ELF64_R_TYPE ((rel + 1)->r_info)) != R_PPC64_TOC)
{
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: unexpected reloc type %u in .opd section"),
ibfd, r_type);
broken = TRUE;
sym_name = bfd_elf_sym_name (ibfd, symtab_hdr, sym,
sym_sec);
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: undefined sym `%s' in .opd section"),
ibfd, sym_name);
broken = TRUE;
if (h != NULL
&& h->root.root.string[0] == '.')
{
- fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, htab);
- if (fdh != NULL
- && fdh->elf.root.type != bfd_link_hash_defined
- && fdh->elf.root.type != bfd_link_hash_defweak)
- fdh = NULL;
+ fdh = ((struct ppc_link_hash_entry *) h)->oh;
+ if (fdh != NULL)
+ {
+ fdh = ppc_follow_link (fdh);
+ if (fdh->elf.root.type != bfd_link_hash_defined
+ && fdh->elf.root.type != bfd_link_hash_defweak)
+ fdh = NULL;
+ }
}
skip = (sym_sec->owner != ibfd
else if (!htab->do_multi_toc)
htab->params->no_multi_toc = 1;
+ /* Default to --no-plt-localentry, as this option can cause problems
+ with symbol interposition. For example, glibc libpthread.so and
+ libc.so duplicate many pthread symbols, with a fallback
+ implementation in libc.so. In some cases the fallback does more
+ work than the pthread implementation. __pthread_condattr_destroy
+ is one such symbol: the libpthread.so implementation is
+ localentry:0 while the libc.so implementation is localentry:8.
+ An app that "cleverly" uses dlopen to only load necessary
+ libraries at runtime may omit loading libpthread.so when not
+ running multi-threaded, which then results in the libc.so
+ fallback symbols being used and ld.so complaining. Now there
+ are workarounds in ld (see non_zero_localentry) to detect the
+ pthread situation, but that may not be the only case where
+ --plt-localentry can cause trouble. */
+ if (htab->params->plt_localentry0 < 0)
+ htab->params->plt_localentry0 = 0;
+ if (htab->params->plt_localentry0
+ && elf_link_hash_lookup (&htab->elf, "GLIBC_2.26",
+ FALSE, FALSE, FALSE) == NULL)
+ info->callbacks->einfo
+ (_("%P: warning: --plt-localentry is especially dangerous without "
+ "ld.so support to detect ABI violations.\n"));
+
htab->tls_get_addr = ((struct ppc_link_hash_entry *)
elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
FALSE, FALSE, TRUE));
&& (tga_fd->type == STT_FUNC
|| tga_fd->needs_plt)
&& !(SYMBOL_CALLS_LOCAL (info, tga_fd)
- || (ELF_ST_VISIBILITY (tga_fd->other) != STV_DEFAULT
- && tga_fd->root.type == bfd_link_hash_undefweak)))
+ || UNDEFWEAK_NO_DYNAMIC_RELOC (info, tga_fd)))
{
struct plt_entry *ent;
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->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->mark = 1;
_bfd_elf_link_hash_hide_symbol (info, opt,
tga->forced_local);
htab->tls_get_addr = (struct ppc_link_hash_entry *) opt;
if (h != NULL
&& h->root.type == bfd_link_hash_undefweak)
ok_tprel = TRUE;
- else
+ else if (sym_sec != NULL
+ && sym_sec->output_section != NULL)
{
value += sym_sec->output_offset;
value += sym_sec->output_section->vma;
case R_PPC64_GOT_TLSLD16_LO:
expecting_tls_get_addr = 1;
found_tls_get_addr_arg = 1;
- /* Fall thru */
+ /* Fall through. */
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TLSGD16_LO:
expecting_tls_get_addr = 1;
found_tls_get_addr_arg = 1;
- /* Fall thru */
+ /* Fall through. */
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_TLSGD:
case R_PPC64_TLSLD:
found_tls_get_addr_arg = 1;
- /* Fall thru */
+ /* Fall through. */
case R_PPC64_TLS:
case R_PPC64_TOC16:
could just mark this symbol to exclude it
from tls optimization but it's safer to skip
the entire optimization. */
+ /* xgettext:c-format */
info->callbacks->minfo (_("%H arg lost __tls_get_addr, "
"TLS optimization disabled\n"),
ibfd, sec, rel->r_offset);
if (toc_ref != NULL)
free (toc_ref);
+ htab->do_tls_opt = 1;
return TRUE;
}
the values of any global symbols in a toc section that has been
edited. Globals in toc sections should be a rarity, so this function
sets a flag if any are found in toc sections other than the one just
- edited, so that futher hash table traversals can be avoided. */
+ edited, so that further hash table traversals can be avoided. */
struct adjust_toc_info
{
if ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0)
{
- (*_bfd_error_handler)
+ _bfd_error_handler
(_("%s defined on removed toc entry"), eh->elf.root.root.string);
do
++i;
return TRUE;
}
-/* Return TRUE iff INSN is one we expect on a _LO variety toc/got reloc. */
+/* Return TRUE iff INSN with a relocation of R_TYPE is one we expect
+ on a _LO variety toc/got reloc. */
static bfd_boolean
-ok_lo_toc_insn (unsigned int insn)
+ok_lo_toc_insn (unsigned int insn, enum elf_ppc64_reloc_type r_type)
{
- return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+ return ((insn & (0x3f << 26)) == 12u << 26 /* addic */
+ || (insn & (0x3f << 26)) == 14u << 26 /* addi */
|| (insn & (0x3f << 26)) == 32u << 26 /* lwz */
|| (insn & (0x3f << 26)) == 34u << 26 /* lbz */
|| (insn & (0x3f << 26)) == 36u << 26 /* stw */
|| (insn & (0x3f << 26)) == 50u << 26 /* lfd */
|| (insn & (0x3f << 26)) == 52u << 26 /* stfs */
|| (insn & (0x3f << 26)) == 54u << 26 /* stfd */
- || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
- && (insn & 3) != 1)
- || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
- && ((insn & 3) == 0 || (insn & 3) == 3))
- || (insn & (0x3f << 26)) == 12u << 26 /* addic */);
+ || (insn & (0x3f << 26)) == 56u << 26 /* lq,lfq */
+ || ((insn & (0x3f << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */
+ /* Exclude lfqu by testing reloc. If relocs are ever
+ defined for the reduced D field in psq_lu then those
+ will need testing too. */
+ && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO)
+ || ((insn & (0x3f << 26)) == 58u << 26 /* ld,lwa */
+ && (insn & 1) == 0)
+ || (insn & (0x3f << 26)) == 60u << 26 /* stfq */
+ || ((insn & (0x3f << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */
+ /* Exclude stfqu. psq_stu as above for psq_lu. */
+ && r_type != R_PPC64_TOC16_LO && r_type != R_PPC64_GOT16_LO)
+ || ((insn & (0x3f << 26)) == 62u << 26 /* std,stq */
+ && (insn & 1) == 0));
}
/* Examine all relocs referencing .toc sections in order to remove
goto error_ret;
if (sym_sec == NULL
+ || sym_sec->output_section == NULL
|| discarded_section (sym_sec))
continue;
}
insn = bfd_get_32 (ibfd, buf);
if (insn_check == check_lo
- ? !ok_lo_toc_insn (insn)
+ ? !ok_lo_toc_insn (insn, r_type)
: ((insn & ((0x3f << 26) | 0x1f << 16))
!= ((15u << 26) | (2 << 16)) /* addis rt,2,imm */))
{
ppc64_elf_tdata (ibfd)->unexpected_toc_insn = 1;
sprintf (str, "%#08x", insn);
info->callbacks->einfo
- (_("%P: %H: toc optimization is not supported for"
+ /* xgettext:c-format */
+ (_("%H: toc optimization is not supported for"
" %s instruction.\n"),
ibfd, sec, rel->r_offset & ~3, str);
}
}
if ((opc & (0x3f << 2)) == (58u << 2))
break;
- /* Fall thru */
+ /* Fall through. */
default:
/* Wrong sort of reloc, or not a ld. We may
/* Merge the used and skip arrays. Assume that TOC
doublewords not appearing as either used or unused belong
- to to an entry more than one doubleword in size. */
+ to an entry more than one doubleword in size. */
for (drop = skip, keep = used, last = 0, some_unused = 0;
drop < skip + (toc->size + 7) / 8;
++drop, ++keep)
if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
ppc_howto_init ();
info->callbacks->einfo
- (_("%P: %H: %s references "
+ /* xgettext:c-format */
+ (_("%H: %s references "
"optimized away TOC entry\n"),
ibfd, sec, rel->r_offset,
ppc64_elf_howto_table[r_type]->name);
if ((skip[i] & (ref_from_discarded | can_optimize)) != 0)
{
if (local_toc_syms)
- (*_bfd_error_handler)
+ _bfd_error_handler
(_("%s defined on removed toc entry"),
bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL));
do
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))
- && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
- || h->root.type != bfd_link_hash_undefweak))
+ || (htab->elf.dynamic_sections_created
+ && h->dynindx != -1
+ && !SYMBOL_REFERENCES_LOCAL (info, h)))
+ && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
{
asection *relgot = ppc64_elf_tdata (gent->owner)->relgot;
relgot->size += rentsize;
}
}
+/* If H is undefined, make it dynamic if that makes sense. */
+
+static bfd_boolean
+ensure_undef_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
+ && ((info->dynamic_undefined_weak != 0
+ && h->root.type == bfd_link_hash_undefweak)
+ || h->root.type == bfd_link_hash_undefined)
+ && 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. */
struct ppc_link_hash_table *htab;
asection *s;
struct ppc_link_hash_entry *eh;
- struct elf_dyn_relocs *p;
struct got_entry **pgent, *gent;
if (h->root.type == bfd_link_hash_indirect)
if (htab == NULL)
return FALSE;
- if ((htab->elf.dynamic_sections_created
- && h->dynindx != -1
- && WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h))
- || h->type == STT_GNU_IFUNC)
- {
- struct plt_entry *pent;
- bfd_boolean doneone = FALSE;
- for (pent = h->plt.plist; pent != NULL; pent = pent->next)
- if (pent->plt.refcount > 0)
- {
- if (!htab->elf.dynamic_sections_created
- || h->dynindx == -1)
- {
- s = htab->elf.iplt;
- pent->plt.offset = s->size;
- s->size += PLT_ENTRY_SIZE (htab);
- s = htab->elf.irelplt;
- }
- else
- {
- /* If this is the first .plt entry, make room for the special
- first entry. */
- s = htab->elf.splt;
- if (s->size == 0)
- s->size += PLT_INITIAL_ENTRY_SIZE (htab);
-
- pent->plt.offset = s->size;
-
- /* Make room for this entry. */
- s->size += PLT_ENTRY_SIZE (htab);
-
- /* Make room for the .glink code. */
- s = htab->glink;
- if (s->size == 0)
- s->size += GLINK_CALL_STUB_SIZE;
- if (htab->opd_abi)
- {
- /* We need bigger stubs past index 32767. */
- if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
- s->size += 4;
- s->size += 2*4;
- }
- else
- s->size += 4;
-
- /* We also need to make an entry in the .rela.plt section. */
- s = htab->elf.srelplt;
- }
- s->size += sizeof (Elf64_External_Rela);
- doneone = TRUE;
- }
- else
- pent->plt.offset = (bfd_vma) -1;
- if (!doneone)
- {
- h->plt.plist = NULL;
- h->needs_plt = 0;
- }
- }
- else
- {
- h->plt.plist = NULL;
- h->needs_plt = 0;
- }
-
eh = (struct ppc_link_hash_entry *) h;
/* Run through the TLS GD got entries first if we're changing them
to TPREL. */
for (gent = h->got.glist; gent != NULL; gent = gent->next)
if (!gent->is_indirect)
{
- /* Make sure this symbol is output as a dynamic symbol.
- Undefined weak syms won't yet be marked as dynamic,
- nor will all TLS symbols. */
- if (h->dynindx == -1
- && !h->forced_local
- && h->type != STT_GNU_IFUNC
- && htab->elf.dynamic_sections_created)
- {
- if (! bfd_elf_link_record_dynamic_symbol (info, h))
- return FALSE;
- }
+ /* Make sure this symbol is output as a dynamic symbol. */
+ if (!ensure_undef_dynamic (info, h))
+ return FALSE;
if (!is_ppc64_elf (gent->owner))
abort ();
allocate_got (h, info, gent);
}
- if (eh->dyn_relocs == NULL
- || (!htab->elf.dynamic_sections_created
- && h->type != STT_GNU_IFUNC))
- return TRUE;
+ /* If no dynamic sections we can't have dynamic relocs, except for
+ IFUNCs which are handled even in static executables. */
+ if (!htab->elf.dynamic_sections_created
+ && h->type != STT_GNU_IFUNC)
+ eh->dyn_relocs = NULL;
- /* In the shared -Bsymbolic case, discard space allocated for
- dynamic pc-relative relocs against symbols which turn out to be
- defined in regular objects. For the normal shared case, discard
- space for relocs that have become local due to symbol visibility
- changes. */
+ /* Also discard relocs on undefined weak syms with non-default
+ visibility, or when dynamic_undefined_weak says so. */
+ else if (UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
+ eh->dyn_relocs = NULL;
- if (bfd_link_pic (info))
+ if (eh->dyn_relocs != NULL)
{
- /* Relocs that use pc_count are those that appear on a call insn,
- or certain REL relocs (see must_be_dyn_reloc) that can be
- generated via assembly. We want calls to protected symbols to
- resolve directly to the function rather than going via the plt.
- If people want function pointer comparisons to work as expected
- then they should avoid writing weird assembly. */
- if (SYMBOL_CALLS_LOCAL (info, h))
+ struct elf_dyn_relocs *p, **pp;
+
+ /* In the shared -Bsymbolic case, discard space allocated for
+ dynamic pc-relative relocs against symbols which turn out to
+ be defined in regular objects. For the normal shared case,
+ discard space for relocs that have become local due to symbol
+ visibility changes. */
+
+ if (bfd_link_pic (info))
{
- struct elf_dyn_relocs **pp;
+ /* Relocs that use pc_count are those that appear on a call
+ insn, or certain REL relocs (see must_be_dyn_reloc) that
+ can be generated via assembly. We want calls to
+ protected symbols to resolve directly to the function
+ rather than going via the plt. If people want function
+ pointer comparisons to work as expected then they should
+ avoid writing weird assembly. */
+ if (SYMBOL_CALLS_LOCAL (info, h))
+ {
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ p->count -= p->pc_count;
+ p->pc_count = 0;
+ if (p->count == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
- for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ if (eh->dyn_relocs != NULL)
{
- p->count -= p->pc_count;
- p->pc_count = 0;
- if (p->count == 0)
- *pp = p->next;
- else
- pp = &p->next;
+ /* Make sure this symbol is output as a dynamic symbol. */
+ if (!ensure_undef_dynamic (info, h))
+ return FALSE;
}
}
-
- /* Also discard relocs on undefined weak syms with non-default
- visibility. */
- if (eh->dyn_relocs != NULL
- && h->root.type == bfd_link_hash_undefweak)
+ else if (h->type == STT_GNU_IFUNC)
{
- if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
- eh->dyn_relocs = NULL;
+ /* A plt entry is always created when making direct calls to
+ an ifunc, even when building a static executable, but
+ that doesn't cover all cases. We may have only an ifunc
+ initialised function pointer for a given ifunc symbol.
- /* 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)
+ For ELFv2, dynamic relocations are not required when
+ generating a global entry PLT stub. */
+ if (abiversion (info->output_bfd) >= 2)
{
- if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ if (global_entry_stub (h))
+ eh->dyn_relocs = NULL;
+ }
+
+ /* For ELFv1 we have function descriptors. Descriptors need
+ to be treated like PLT entries and thus have dynamic
+ relocations. One exception is when the function
+ descriptor is copied into .dynbss (which should only
+ happen with ancient versions of gcc). */
+ else if (h->needs_copy)
+ eh->dyn_relocs = NULL;
+ }
+ else if (ELIMINATE_COPY_RELOCS)
+ {
+ /* 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. */
+ if (!ensure_undef_dynamic (info, h))
return FALSE;
+
+ if (h->dynindx == -1)
+ eh->dyn_relocs = NULL;
}
+ else
+ eh->dyn_relocs = NULL;
+ }
+
+ /* Finally, allocate space. */
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *sreloc = elf_section_data (p->sec)->sreloc;
+ if (eh->elf.type == STT_GNU_IFUNC)
+ sreloc = htab->elf.irelplt;
+ sreloc->size += p->count * sizeof (Elf64_External_Rela);
}
}
- else if (h->type == STT_GNU_IFUNC)
- {
- if (!h->non_got_ref)
- eh->dyn_relocs = NULL;
- }
- else if (ELIMINATE_COPY_RELOCS)
+
+ if ((htab->elf.dynamic_sections_created
+ && h->dynindx != -1)
+ || h->type == STT_GNU_IFUNC)
{
- /* For the non-shared case, discard space for relocs against
- symbols which turn out to need copy relocs or are not
- dynamic. */
+ struct plt_entry *pent;
+ bfd_boolean doneone = FALSE;
+ for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+ if (pent->plt.refcount > 0)
+ {
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
+ {
+ s = htab->elf.iplt;
+ pent->plt.offset = s->size;
+ s->size += PLT_ENTRY_SIZE (htab);
+ s = htab->elf.irelplt;
+ }
+ else
+ {
+ /* If this is the first .plt entry, make room for the special
+ first entry. */
+ s = htab->elf.splt;
+ if (s->size == 0)
+ s->size += PLT_INITIAL_ENTRY_SIZE (htab);
+
+ pent->plt.offset = s->size;
+
+ /* Make room for this entry. */
+ s->size += PLT_ENTRY_SIZE (htab);
+
+ /* Make room for the .glink code. */
+ s = htab->glink;
+ if (s->size == 0)
+ s->size += GLINK_CALL_STUB_SIZE;
+ if (htab->opd_abi)
+ {
+ /* We need bigger stubs past index 32767. */
+ if (s->size >= GLINK_CALL_STUB_SIZE + 32768*2*4)
+ s->size += 4;
+ s->size += 2*4;
+ }
+ else
+ s->size += 4;
- if (!h->non_got_ref
- && !h->def_regular)
+ /* We also need to make an entry in the .rela.plt section. */
+ s = htab->elf.srelplt;
+ }
+ s->size += sizeof (Elf64_External_Rela);
+ doneone = TRUE;
+ }
+ else
+ pent->plt.offset = (bfd_vma) -1;
+ if (!doneone)
{
- /* Make sure this symbol is output as a dynamic symbol.
- Undefined weak syms won't yet be marked as dynamic. */
- if (h->dynindx == -1
- && !h->forced_local)
- {
- if (! bfd_elf_link_record_dynamic_symbol (info, h))
- return FALSE;
- }
-
- /* If that succeeded, we know we'll be keeping all the
- relocs. */
- if (h->dynindx != -1)
- goto keep;
+ h->plt.plist = NULL;
+ h->needs_plt = 0;
}
-
- eh->dyn_relocs = NULL;
-
- keep: ;
}
-
- /* Finally, allocate space. */
- for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ else
{
- asection *sreloc = elf_section_data (p->sec)->sreloc;
- if (eh->elf.type == STT_GNU_IFUNC)
- sreloc = htab->elf.irelplt;
- sreloc->size += p->count * sizeof (Elf64_External_Rela);
+ h->plt.plist = NULL;
+ h->needs_plt = 0;
}
return TRUE;
need to define the symbol in the executable on a call stub.
This is to avoid text relocations. */
s->size = (s->size + 15) & -16;
+ h->root.type = bfd_link_hash_defined;
h->root.u.def.section = s;
h->root.u.def.value = s->size;
s->size += 16;
|| 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;
&& (stub_entry->h == htab->tls_get_addr_fd
|| stub_entry->h == htab->tls_get_addr)
&& htab->params->tls_get_addr_opt)
- size += 13 * 4;
+ {
+ size += 7 * 4;
+ if (ALWAYS_EMIT_R2SAVE
+ || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+ size += 6 * 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. */
+/* Depending on the sign of plt_stub_align:
+ If positive, return the padding to align to a 2**plt_stub_align
+ boundary.
+ If negative, 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->params->plt_stub_align;
+ int stub_align;
unsigned stub_size = plt_stub_size (htab, stub_entry, plt_off);
bfd_vma stub_off = stub_entry->group->stub_sec->size;
+ if (htab->params->plt_stub_align >= 0)
+ {
+ stub_align = 1 << htab->params->plt_stub_align;
+ if ((stub_off & (stub_align - 1)) != 0)
+ return stub_align - (stub_off & (stub_align - 1));
+ return 0;
+ }
+
+ stub_align = 1 << -htab->params->plt_stub_align;
if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
> ((stub_size - 1) & -stub_align))
return stub_align - (stub_off & (stub_align - 1));
bfd_put_32 (obfd, ADD_R3_R12_R13, p), p += 4;
bfd_put_32 (obfd, BEQLR, p), p += 4;
bfd_put_32 (obfd, MR_R3_R0, p), p += 4;
+ if (r != NULL)
+ r[0].r_offset += 7 * 4;
+ if (!ALWAYS_EMIT_R2SAVE
+ && stub_entry->stub_type != ppc_stub_plt_call_r2save)
+ return build_plt_stub (htab, stub_entry, p, offset, r);
+
bfd_put_32 (obfd, MFLR_R11, p), p += 4;
bfd_put_32 (obfd, STD_R11_0R1 + STK_LINKER (htab), p), p += 4;
if (r != NULL)
- r[0].r_offset += 9 * 4;
+ r[0].r_offset += 2 * 4;
p = build_plt_stub (htab, stub_entry, p, offset, r);
bfd_put_32 (obfd, BCTRL, p - 4);
/* If the old-ABI "dot-symbol" is undefined make it weak so
we don't get a link error from RELOC_FOR_GLOBAL_SYMBOL. */
- if (fh->elf.root.type == bfd_link_hash_undefined)
+ if (fh->elf.root.type == bfd_link_hash_undefined
+ && (stub_entry->h->elf.root.type == bfd_link_hash_defined
+ || stub_entry->h->elf.root.type == bfd_link_hash_defweak))
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. */
* sizeof (Elf64_External_Rela)));
bfd_elf64_swap_reloca_out (info->output_bfd, &rela, rl);
stub_entry->plt_ent->plt.offset |= 1;
+ htab->local_ifunc_resolver = 1;
}
off = (dest
if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
{
info->callbacks->einfo
+ /* xgettext:c-format */
(_("%P: linkage table error against `%T'\n"),
stub_entry->h != NULL
? stub_entry->h->elf.root.root.string
- htab->sec_info[stub_entry->group->link_sec->id].toc_off);
size = plt_stub_size (htab, stub_entry, off);
+ if (stub_entry->h != NULL
+ && (stub_entry->h == htab->tls_get_addr_fd
+ || stub_entry->h == htab->tls_get_addr)
+ && htab->params->tls_get_addr_opt
+ && (ALWAYS_EMIT_R2SAVE
+ || stub_entry->stub_type == ppc_stub_plt_call_r2save))
+ stub_entry->group->tls_get_addr_opt_bctrl
+ = stub_entry->group->stub_sec->size + size - 5 * 4;
+
if (htab->params->plt_stub_align)
size += plt_stub_pad (htab, stub_entry, off);
if (info->emitrelocations)
{
struct ppc_link_hash_table *htab;
asection *osec;
- bfd_size_type stub14_group_size;
bfd_boolean suppress_size_errors;
htab = ppc_hash_table (info);
return FALSE;
suppress_size_errors = FALSE;
- stub14_group_size = stub_group_size >> 10;
if (stub_group_size == 1)
{
/* Default values. */
if (stubs_always_before_branch)
- {
- stub_group_size = 0x1e00000;
- stub14_group_size = 0x7800;
- }
+ stub_group_size = 0x1e00000;
else
- {
- stub_group_size = 0x1c00000;
- stub14_group_size = 0x7000;
- }
+ stub_group_size = 0x1c00000;
suppress_size_errors = TRUE;
}
bfd_boolean big_sec;
bfd_vma curr_toc;
struct map_stub *group;
+ bfd_size_type group_size;
curr = tail;
total = tail->size;
- big_sec = total > (ppc64_elf_section_data (tail) != NULL
- && ppc64_elf_section_data (tail)->has_14bit_branch
- ? stub14_group_size : stub_group_size);
+ group_size = (ppc64_elf_section_data (tail) != NULL
+ && ppc64_elf_section_data (tail)->has_14bit_branch
+ ? stub_group_size >> 10 : stub_group_size);
+
+ big_sec = total > group_size;
if (big_sec && !suppress_size_errors)
- (*_bfd_error_handler) (_("%B section %A exceeds stub group size"),
- tail->owner, tail);
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%B section %A exceeds stub group size"),
+ tail->owner, tail);
curr_toc = htab->sec_info[tail->id].toc_off;
while ((prev = htab->sec_info[curr->id].u.list) != NULL
&& ((total += curr->output_offset - prev->output_offset)
< (ppc64_elf_section_data (prev) != NULL
&& ppc64_elf_section_data (prev)->has_14bit_branch
- ? stub14_group_size : stub_group_size))
+ ? (group_size = stub_group_size >> 10) : group_size))
&& htab->sec_info[prev->id].toc_off == curr_toc)
curr = prev;
/* OK, the size from the start of CURR to the end is less
- than stub_group_size and thus can be handled by one stub
+ than group_size and thus can be handled by one stub
section. (or the tail section is itself larger than
- stub_group_size, in which case we may be toast.) We
- should really be keeping track of the total size of stubs
- added here, as stubs contribute to the final output
- section size. That's a little tricky, and this way will
- only break if stubs added make the total size more than
- 2^25, ie. for the default stub_group_size, if stubs total
- more than 2097152 bytes, or nearly 75000 plt call stubs. */
+ group_size, in which case we may be toast.) We should
+ really be keeping track of the total size of stubs added
+ here, as stubs contribute to the final output section
+ size. That's a little tricky, and this way will only
+ break if stubs added make the total size more than 2^25,
+ ie. for the default stub_group_size, if stubs total more
+ than 2097152 bytes, or nearly 75000 plt call stubs. */
group = bfd_alloc (curr->owner, sizeof (*group));
if (group == NULL)
return FALSE;
group->link_sec = curr;
group->stub_sec = NULL;
group->needs_save_res = 0;
+ group->tls_get_addr_opt_bctrl = -1u;
group->next = htab->group;
htab->group = group;
do
}
while (tail != curr && (tail = prev) != NULL);
- /* But wait, there's more! Input sections up to stub_group_size
+ /* But wait, there's more! Input sections up to group_size
bytes before the stub section can be handled by it too.
Don't do this if we have a really large section after the
stubs, as adding more stubs increases the chance that
&& ((total += tail->output_offset - prev->output_offset)
< (ppc64_elf_section_data (prev) != NULL
&& ppc64_elf_section_data (prev)->has_14bit_branch
- ? stub14_group_size : stub_group_size))
+ ? (group_size = stub_group_size >> 10) : group_size))
&& htab->sec_info[prev->id].toc_off == curr_toc)
{
tail = prev;
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. */
- 0, 0, 0, 0
+ DW_CFA_def_cfa, 1, 0 /* def_cfa: r1 offset 0. */
};
+static size_t
+stub_eh_frame_size (struct map_stub *group, size_t align)
+{
+ size_t this_size = 17;
+ if (group->tls_get_addr_opt_bctrl != -1u)
+ {
+ unsigned int to_bctrl = group->tls_get_addr_opt_bctrl / 4;
+ if (to_bctrl < 64)
+ this_size += 1;
+ else if (to_bctrl < 256)
+ this_size += 2;
+ else if (to_bctrl < 65536)
+ this_size += 3;
+ else
+ this_size += 5;
+ this_size += 6;
+ }
+ this_size = (this_size + align - 1) & -align;
+ return this_size;
+}
+
/* 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
if (!group_sections (info, stub_group_size, stubs_always_before_branch))
return FALSE;
+#define STUB_SHRINK_ITER 20
+ /* Loop until no stubs added. After iteration 20 of this loop we may
+ exit on a stub section shrinking. This is to break out of a
+ pathological case where adding stubs on one iteration decreases
+ section gaps (perhaps due to alignment), which then requires
+ fewer or smaller stubs on the next iteration. */
+
while (1)
{
bfd *input_bfd;
unsigned int bfd_indx;
struct map_stub *group;
- asection *stub_sec;
htab->stub_iteration += 1;
if (hash == NULL)
{
sym_value = sym->st_value;
- ok_dest = TRUE;
+ if (sym_sec != NULL
+ && sym_sec->output_section != NULL)
+ ok_dest = TRUE;
}
else if (hash->elf.root.type == bfd_link_hash_defined
|| hash->elf.root.type == bfd_link_hash_defweak)
use the func descriptor sym instead if it is
defined. */
if (hash->elf.root.root.string[0] == '.'
- && (fdh = lookup_fdh (hash, htab)) != NULL)
+ && hash->oh != NULL)
{
+ fdh = ppc_follow_link (hash->oh);
if (fdh->elf.root.type == bfd_link_hash_defined
|| fdh->elf.root.type == bfd_link_hash_defweak)
{
continue;
}
- if (stub_type == ppc_stub_plt_call
- && irela + 1 < irelaend
- && irela[1].r_offset == irela->r_offset + 4
- && ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE)
+ if (stub_type == ppc_stub_plt_call)
{
- if (!tocsave_find (htab, INSERT,
- &local_syms, irela + 1, input_bfd))
- goto error_ret_free_internal;
+ if (!htab->opd_abi
+ && htab->params->plt_localentry0 != 0
+ && is_elfv2_localentry0 (&hash->elf))
+ htab->has_plt_localentry0 = 1;
+ else if (irela + 1 < irelaend
+ && irela[1].r_offset == irela->r_offset + 4
+ && (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
+ stub_type = ppc_stub_plt_call_r2save;
}
- else if (stub_type == ppc_stub_plt_call)
- stub_type = ppc_stub_plt_call_r2save;
/* Support for grouping stub sections. */
id_sec = htab->sec_info[section->id].u.group->link_sec;
/* We may have added some stubs. Find out the new size of the
stub sections. */
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->stub_sec != NULL)
{
- stub_sec->rawsize = stub_sec->size;
+ asection *stub_sec = group->stub_sec;
+
+ if (htab->stub_iteration <= STUB_SHRINK_ITER
+ || stub_sec->rawsize < stub_sec->size)
+ /* Past STUB_SHRINK_ITER, rawsize is the max size seen. */
+ stub_sec->rawsize = stub_sec->size;
stub_sec->size = 0;
stub_sec->reloc_count = 0;
stub_sec->flags &= ~SEC_RELOC;
&& !bfd_is_abs_section (htab->glink_eh_frame->output_section)
&& htab->glink_eh_frame->output_section->size != 0)
{
- size_t size = 0, align;
+ size_t size = 0, align = 4;
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
- size += 24;
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->stub_sec != NULL)
+ size += stub_eh_frame_size (group, align);
if (htab->glink != NULL && htab->glink->size != 0)
- size += 24;
+ size += (24 + align - 1) & -align;
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;
+ size += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+ align = 1ul << htab->glink_eh_frame->output_section->alignment_power;
+ size = (size + align - 1) & -align;
htab->glink_eh_frame->rawsize = htab->glink_eh_frame->size;
htab->glink_eh_frame->size = size;
}
if (htab->params->plt_stub_align != 0)
- for (stub_sec = htab->params->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->params->plt_stub_align) - 1)
- & (-1 << htab->params->plt_stub_align));
-
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0
- && stub_sec->rawsize != stub_sec->size)
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->stub_sec != NULL)
+ group->stub_sec->size = ((group->stub_sec->size
+ + (1 << htab->params->plt_stub_align) - 1)
+ & -(1 << htab->params->plt_stub_align));
+
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->stub_sec != NULL
+ && group->stub_sec->rawsize != group->stub_sec->size
+ && (htab->stub_iteration <= STUB_SHRINK_ITER
+ || group->stub_sec->rawsize < group->stub_sec->size))
break;
- /* Exit from this loop when no stubs have been added, and no stubs
- have changed size. */
- if (stub_sec == NULL
+ if (group == NULL
&& (htab->glink_eh_frame == NULL
|| htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size))
break;
bfd_vma val;
bfd_byte *p, *last_fde;
size_t last_fde_len, size, align, pad;
- asection *stub_sec;
+ struct map_stub *group;
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;
+ align = 4;
memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie));
/* CIE length (rewrite in case little-endian). */
- last_fde_len = sizeof (glink_eh_frame_cie) - 4;
+ last_fde_len = ((sizeof (glink_eh_frame_cie) + align - 1) & -align) - 4;
bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
- p += sizeof (glink_eh_frame_cie);
+ p += last_fde_len + 4;
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->stub_sec != NULL)
{
last_fde = p;
- last_fde_len = 20;
+ last_fde_len = stub_eh_frame_size (group, align) - 4;
/* FDE length. */
- bfd_put_32 (htab->elf.dynobj, 20, p);
+ bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
p += 4;
/* CIE pointer. */
val = p - htab->glink_eh_frame->contents;
/* Offset to stub section, written later. */
p += 4;
/* stub section size. */
- bfd_put_32 (htab->elf.dynobj, stub_sec->size, p);
+ bfd_put_32 (htab->elf.dynobj, group->stub_sec->size, p);
p += 4;
/* Augmentation. */
p += 1;
+ if (group->tls_get_addr_opt_bctrl != -1u)
+ {
+ unsigned int to_bctrl = group->tls_get_addr_opt_bctrl / 4;
+
+ /* This FDE needs more than just the default.
+ Describe __tls_get_addr_opt stub LR. */
+ if (to_bctrl < 64)
+ *p++ = DW_CFA_advance_loc + to_bctrl;
+ else if (to_bctrl < 256)
+ {
+ *p++ = DW_CFA_advance_loc1;
+ *p++ = to_bctrl;
+ }
+ else if (to_bctrl < 65536)
+ {
+ *p++ = DW_CFA_advance_loc2;
+ bfd_put_16 (htab->elf.dynobj, to_bctrl, p);
+ p += 2;
+ }
+ else
+ {
+ *p++ = DW_CFA_advance_loc4;
+ bfd_put_32 (htab->elf.dynobj, to_bctrl, p);
+ p += 4;
+ }
+ *p++ = DW_CFA_offset_extended_sf;
+ *p++ = 65;
+ *p++ = -(STK_LINKER (htab) / 8) & 0x7f;
+ *p++ = DW_CFA_advance_loc + 4;
+ *p++ = DW_CFA_restore_extended;
+ *p++ = 65;
+ }
/* Pad. */
- p += 7;
+ p = last_fde + last_fde_len + 4;
}
if (htab->glink != NULL && htab->glink->size != 0)
{
last_fde = p;
- last_fde_len = 20;
+ last_fde_len = ((24 + align - 1) & -align) - 4;
/* FDE length. */
- bfd_put_32 (htab->elf.dynobj, 20, p);
+ bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
p += 4;
/* CIE pointer. */
val = p - htab->glink_eh_frame->contents;
*p++ = DW_CFA_advance_loc + 1;
*p++ = DW_CFA_register;
*p++ = 65;
- *p++ = 12;
- *p++ = DW_CFA_advance_loc + 4;
+ *p++ = htab->opd_abi ? 12 : 0;
+ *p++ = DW_CFA_advance_loc + (htab->opd_abi ? 5 : 7);
*p++ = DW_CFA_restore_extended;
*p++ = 65;
+ p += ((24 + align - 1) & -align) - 24;
}
/* 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. */
+ align = 1ul << htab->glink_eh_frame->output_section->alignment_power;
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;
+ pad = ((size + align - 1) & -align) - size;
htab->glink_eh_frame->size = size + pad;
bfd_put_32 (htab->elf.dynobj, last_fde_len + pad, last_fde);
}
return FALSE;
/* Allocate memory to hold the linker stubs. */
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0
+ for (group = htab->group; group != NULL; group = group->next)
+ if ((stub_sec = group->stub_sec) != NULL
&& stub_sec->size != 0)
{
stub_sec->contents = bfd_zalloc (htab->params->stub_bfd, stub_sec->size);
if (stub_sec->contents == NULL)
return FALSE;
- /* We want to check that built size is the same as calculated
- size. rawsize is a convenient location to use. */
- stub_sec->rawsize = stub_sec->size;
stub_sec->size = 0;
}
p += 4;
bfd_put_32 (htab->glink->owner, MFLR_R11, p);
p += 4;
+ bfd_put_32 (htab->glink->owner, STD_R2_0R1 + 24, p);
+ p += 4;
bfd_put_32 (htab->glink->owner, LD_R2_0R11 | (-16 & 0xfffc), p);
p += 4;
bfd_put_32 (htab->glink->owner, MTLR_R0, p);
htab->relbrlt->reloc_count = 0;
if (htab->params->plt_stub_align != 0)
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ for (group = htab->group; group != NULL; group = group->next)
+ if ((stub_sec = group->stub_sec) != NULL)
stub_sec->size = ((stub_sec->size
+ (1 << htab->params->plt_stub_align) - 1)
- & (-1 << htab->params->plt_stub_align));
+ & -(1 << htab->params->plt_stub_align));
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ for (group = htab->group; group != NULL; group = group->next)
+ if ((stub_sec = group->stub_sec) != NULL)
{
stub_sec_count += 1;
- if (stub_sec->rawsize != stub_sec->size)
+ if (stub_sec->rawsize != stub_sec->size
+ && (htab->stub_iteration <= STUB_SHRINK_ITER
+ || stub_sec->rawsize < stub_sec->size))
break;
}
/* Note that the glink_eh_frame check here is not only testing that
the generated size matched the calculated size but also that
bfd_elf_discard_info didn't make any changes to the section. */
- if (stub_sec != NULL
+ if (group != NULL
|| (htab->glink_eh_frame != NULL
&& htab->glink_eh_frame->rawsize != htab->glink_eh_frame->size))
{
return TRUE;
}
-/* This function undoes the changes made by add_symbol_adjust. */
-
-static bfd_boolean
-undo_symbol_twiddle (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
-{
- struct ppc_link_hash_entry *eh;
-
- if (h->root.type == bfd_link_hash_indirect)
- return TRUE;
-
- eh = (struct ppc_link_hash_entry *) h;
- if (eh->elf.root.type != bfd_link_hash_undefweak || !eh->was_undefined)
- return TRUE;
-
- eh->elf.root.type = bfd_link_hash_undefined;
- return TRUE;
-}
-
-void
-ppc64_elf_restore_symbols (struct bfd_link_info *info)
-{
- struct ppc_link_hash_table *htab = ppc_hash_table (info);
-
- if (htab != NULL)
- elf_link_hash_traverse (&htab->elf, undo_symbol_twiddle, info);
-}
-
/* What to do when ld finds relocations against symbols defined in
discarded sections. */
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
Elf_Internal_Rela *rel;
+ Elf_Internal_Rela *wrel;
Elf_Internal_Rela *relend;
Elf_Internal_Rela outrel;
bfd_byte *loc;
bfd_boolean is_opd;
/* Assume 'at' branch hints. */
bfd_boolean is_isa_v2 = TRUE;
- bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
+ bfd_vma d_offset = (bfd_big_endian (input_bfd) ? 2 : 0);
/* Initialize howto table if needed. */
if (!ppc64_elf_howto_table[R_PPC64_ADDR32])
sym_hashes = elf_sym_hashes (input_bfd);
is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
- rel = relocs;
+ rel = wrel = relocs;
relend = relocs + input_section->reloc_count;
- for (; rel < relend; rel++)
+ for (; rel < relend; wrel++, rel++)
{
enum elf_ppc64_reloc_type r_type;
bfd_vma addend;
struct ppc_stub_hash_entry *stub_entry;
bfd_vma max_br_offset;
bfd_vma from;
- const Elf_Internal_Rela orig_rel = *rel;
+ Elf_Internal_Rela orig_rel;
reloc_howto_type *howto;
struct reloc_howto_struct alt_howto;
+ again:
+ orig_rel = *rel;
+
r_type = ELF64_R_TYPE (rel->r_info);
r_symndx = ELF64_R_SYM (rel->r_info);
symbol of the previous ADDR64 reloc. The symbol gives us the
proper TOC base to use. */
if (rel->r_info == ELF64_R_INFO (0, R_PPC64_TOC)
- && rel != relocs
- && ELF64_R_TYPE (rel[-1].r_info) == R_PPC64_ADDR64
+ && wrel != relocs
+ && ELF64_R_TYPE (wrel[-1].r_info) == R_PPC64_ADDR64
&& is_opd)
- r_symndx = ELF64_R_SYM (rel[-1].r_info);
+ r_symndx = ELF64_R_SYM (wrel[-1].r_info);
sym = NULL;
sec = NULL;
h = (struct ppc_link_hash_entry *) h_elf;
if (sec != NULL && discarded_section (sec))
- RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
- rel, 1, relend,
- ppc64_elf_howto_table[r_type], 0,
- contents);
+ {
+ _bfd_clear_contents (ppc64_elf_howto_table[r_type],
+ input_bfd, input_section,
+ contents + rel->r_offset);
+ wrel->r_offset = rel->r_offset;
+ wrel->r_info = 0;
+ wrel->r_addend = 0;
+
+ /* For ld -r, remove relocations in debug sections against
+ sections defined in discarded sections. Not done for
+ non-debug to preserve relocs in .eh_frame which the
+ eh_frame editing code expects to be present. */
+ if (bfd_link_relocatable (info)
+ && (input_section->flags & SEC_DEBUGGING))
+ wrel--;
+
+ continue;
+ }
if (bfd_link_relocatable (info))
- continue;
+ goto copy_reloc;
if (h != NULL && &h->elf == htab->elf.hgot)
{
else
info->callbacks->einfo
(!IS_PPC64_TLS_RELOC (r_type)
- ? _("%P: %H: %s used with TLS symbol `%T'\n")
- : _("%P: %H: %s used with non-TLS symbol `%T'\n"),
+ /* xgettext:c-format */
+ ? _("%H: %s used with TLS symbol `%T'\n")
+ /* xgettext:c-format */
+ : _("%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);
break;
case R_PPC64_LO_DS_OPT:
- insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+ insn = bfd_get_32 (input_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);
+ bfd_put_32 (input_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;
&& (tls_mask & TLS_TPREL) == 0)
{
rel->r_offset -= d_offset;
- bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
+ bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
r_type = R_PPC64_NONE;
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
}
&& (tls_mask & TLS_TPREL) == 0)
{
toctprel:
- insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+ insn = bfd_get_32 (input_bfd,
+ contents + rel->r_offset - d_offset);
insn &= 31 << 21;
insn |= 0x3c0d0000; /* addis 0,13,0 */
- bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
+ bfd_put_32 (input_bfd, insn,
+ contents + rel->r_offset - d_offset);
r_type = R_PPC64_TPREL16_HA;
if (toc_symndx != 0)
{
rel->r_addend = toc_addend;
/* We changed the symbol. Start over in order to
get h, sym, sec etc. right. */
- rel--;
- continue;
+ goto again;
}
else
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
if (tls_mask != 0
&& (tls_mask & TLS_TPREL) == 0)
{
- insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
+ insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
insn = _bfd_elf_ppc_at_tls_transform (insn, 13);
if (insn == 0)
abort ();
- bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
/* Was PPC64_TLS which sits on insn boundary, now
PPC64_TPREL16_LO which is at low-order half-word. */
rel->r_offset += d_offset;
rel->r_addend = toc_addend;
/* We changed the symbol. Start over in order to
get h, sym, sec etc. right. */
- rel--;
- continue;
+ goto again;
}
else
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
else
{
rel->r_offset -= d_offset;
- bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
+ bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
r_type = R_PPC64_NONE;
}
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
case R_PPC64_GOT_TLSLD16_LO:
if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
{
- unsigned int insn1, insn2, insn3;
+ unsigned int insn1, insn2;
bfd_vma offset;
tls_ldgd_opt:
need to keep the destination reg. It may be
something other than the usual r3, and moved to r3
before the call by intervening code. */
- insn1 = bfd_get_32 (output_bfd,
+ insn1 = bfd_get_32 (input_bfd,
contents + rel->r_offset - d_offset);
if ((tls_mask & tls_gd) != 0)
{
rel[1].r_addend = rel->r_addend;
}
}
- bfd_put_32 (output_bfd, insn1,
+ bfd_put_32 (input_bfd, insn1,
contents + rel->r_offset - d_offset);
if (offset != (bfd_vma) -1)
- {
- insn3 = bfd_get_32 (output_bfd,
- contents + offset + 4);
- if (insn3 == NOP
- || insn3 == CROR_151515 || insn3 == CROR_313131)
- {
- rel[1].r_offset += 4;
- bfd_put_32 (output_bfd, insn2, contents + offset + 4);
- insn2 = NOP;
- }
- bfd_put_32 (output_bfd, insn2, contents + offset);
- }
+ bfd_put_32 (input_bfd, insn2, contents + offset);
if ((tls_mask & tls_gd) == 0
&& (tls_gd == 0 || toc_symndx != 0))
{
/* We changed the symbol. Start over in order
to get h, sym, sec etc. right. */
- rel--;
- continue;
+ goto again;
}
}
break;
case R_PPC64_TLSGD:
if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
{
- unsigned int insn2, insn3;
+ unsigned int insn2;
bfd_vma offset = rel->r_offset;
if ((tls_mask & TLS_TPRELGD) != 0)
/* Zap the reloc on the _tls_get_addr call too. */
BFD_ASSERT (offset == rel[1].r_offset);
rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
- insn3 = bfd_get_32 (output_bfd,
- contents + offset + 4);
- if (insn3 == NOP
- || insn3 == CROR_151515 || insn3 == CROR_313131)
- {
- rel->r_offset += 4;
- bfd_put_32 (output_bfd, insn2, contents + offset + 4);
- insn2 = NOP;
- }
- bfd_put_32 (output_bfd, insn2, contents + offset);
+ bfd_put_32 (input_bfd, insn2, contents + offset);
if ((tls_mask & TLS_TPRELGD) == 0 && toc_symndx != 0)
- {
- rel--;
- continue;
- }
+ goto again;
}
break;
case R_PPC64_TLSLD:
if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
{
- unsigned int insn2, insn3;
+ unsigned int insn2;
bfd_vma offset = rel->r_offset;
if (toc_symndx)
BFD_ASSERT (offset == rel[1].r_offset);
rel[1].r_info = ELF64_R_INFO (STN_UNDEF, R_PPC64_NONE);
insn2 = 0x38630000; /* addi 3,3,0 */
- insn3 = bfd_get_32 (output_bfd,
- contents + offset + 4);
- if (insn3 == NOP
- || insn3 == CROR_151515 || insn3 == CROR_313131)
- {
- rel->r_offset += 4;
- bfd_put_32 (output_bfd, insn2, contents + offset + 4);
- insn2 = NOP;
- }
- bfd_put_32 (output_bfd, insn2, contents + offset);
- rel--;
- continue;
+ bfd_put_32 (input_bfd, insn2, contents + offset);
+ goto again;
}
break;
}
break;
+ case R_PPC64_ENTRY:
+ relocation = TOCstart + htab->sec_info[input_section->id].toc_off;
+ if (!bfd_link_pic (info)
+ && !info->traditional_format
+ && relocation + 0x80008000 <= 0xffffffff)
+ {
+ unsigned int insn1, insn2;
+
+ insn1 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn2 = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+ if ((insn1 & ~0xfffc) == LD_R2_0R12
+ && insn2 == ADD_R2_R2_R12)
+ {
+ bfd_put_32 (input_bfd,
+ LIS_R2 + PPC_HA (relocation),
+ contents + rel->r_offset);
+ bfd_put_32 (input_bfd,
+ ADDI_R2_R2 + PPC_LO (relocation),
+ contents + rel->r_offset + 4);
+ }
+ }
+ else
+ {
+ relocation -= (rel->r_offset
+ + input_section->output_offset
+ + input_section->output_section->vma);
+ if (relocation + 0x80008000 <= 0xffffffff)
+ {
+ unsigned int insn1, insn2;
+
+ insn1 = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn2 = bfd_get_32 (input_bfd, contents + rel->r_offset + 4);
+ if ((insn1 & ~0xfffc) == LD_R2_0R12
+ && insn2 == ADD_R2_R2_R12)
+ {
+ bfd_put_32 (input_bfd,
+ ADDIS_R2_R12 + PPC_HA (relocation),
+ contents + rel->r_offset);
+ bfd_put_32 (input_bfd,
+ ADDI_R2_R2 + PPC_LO (relocation),
+ contents + rel->r_offset + 4);
+ }
+ }
+ }
+ break;
+
case R_PPC64_REL16_HA:
/* If we are generating a non-PIC executable, edit
. 0: addis 2,12,.TOC.-0b@ha
if .TOC. is in range. */
if (!bfd_link_pic (info)
&& !info->traditional_format
+ && !htab->opd_abi
+ && rel->r_addend == d_offset
&& h != NULL && &h->elf == htab->elf.hgot
&& rel + 1 < relend
&& rel[1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_REL16_LO)
{
unsigned int insn1, insn2;
bfd_vma offset = rel->r_offset - d_offset;
- insn1 = bfd_get_32 (output_bfd, contents + offset);
- insn2 = bfd_get_32 (output_bfd, contents + offset + 4);
- if ((insn1 & 0xffff0000) == 0x3c4c0000 /* addis 2,12 */
- && (insn2 & 0xffff0000) == 0x38420000 /* addi 2,2 */)
+ insn1 = bfd_get_32 (input_bfd, contents + offset);
+ insn2 = bfd_get_32 (input_bfd, contents + offset + 4);
+ if ((insn1 & 0xffff0000) == ADDIS_R2_R12
+ && (insn2 & 0xffff0000) == ADDI_R2_R2)
{
r_type = R_PPC64_ADDR16_HA;
rel->r_info = ELF64_R_INFO (r_symndx, r_type);
rel->r_addend -= d_offset;
rel[1].r_info = ELF64_R_INFO (r_symndx, R_PPC64_ADDR16_LO);
rel[1].r_addend -= d_offset + 4;
- bfd_put_32 (output_bfd, 0x3c400000, contents + offset);
+ bfd_put_32 (input_bfd, LIS_R2, contents + offset);
}
}
break;
case R_PPC64_ADDR14_BRTAKEN:
case R_PPC64_REL14_BRTAKEN:
insn = 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field. */
- /* Fall thru. */
+ /* Fall through. */
/* Branch not taken prediction relocations. */
case R_PPC64_ADDR14_BRNTAKEN:
case R_PPC64_REL14_BRNTAKEN:
- insn |= bfd_get_32 (output_bfd,
+ insn |= bfd_get_32 (input_bfd,
contents + rel->r_offset) & ~(0x01 << 21);
- /* Fall thru. */
+ /* Fall through. */
case R_PPC64_REL14:
max_br_offset = 1 << 15;
- /* Fall thru. */
+ /* Fall through. */
case R_PPC64_REL24:
/* Calls to functions with a different TOC, such as calls to
{
bfd_boolean can_plt_call = FALSE;
- /* All of these stubs will modify r2, so there must be a
+ if (stub_entry->stub_type == ppc_stub_plt_call
+ && !htab->opd_abi
+ && htab->params->plt_localentry0 != 0
+ && is_elfv2_localentry0 (&h->elf))
+ {
+ /* The function doesn't use or change r2. */
+ can_plt_call = TRUE;
+ }
+
+ /* All of these stubs may modify r2, so there must be a
branch and link followed by a nop. The nop is
replaced by an insn to restore r2. */
- if (rel->r_offset + 8 <= input_section->size)
+ else if (rel->r_offset + 8 <= input_section->size)
{
unsigned long br;
if (stub_entry->stub_type == ppc_stub_plt_call
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
info->callbacks->einfo
- (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
+ /* xgettext:c-format */
+ (_("%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
- (_("%P: %H: call to `%T' lacks nop, can't restore toc; "
+ /* xgettext:c-format */
+ (_("%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);
addend = 0;
reloc_dest = DEST_STUB;
- 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)
insn ^= 0x01 << 21;
}
- bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
}
/* NOP out calls to undefined weak functions.
&& relocation == 0
&& addend == 0)
{
- bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
- continue;
+ bfd_put_32 (input_bfd, NOP, contents + rel->r_offset);
+ goto copy_reloc;
}
break;
}
{
default:
info->callbacks->einfo
+ /* xgettext:c-format */
(_("%P: %B: unknown relocation type %d for `%T'\n"),
input_bfd, (int) r_type, sym_name);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
- continue;
+ goto copy_reloc;
case R_PPC64_NONE:
case R_PPC64_TLS:
case R_PPC64_TOCSAVE:
case R_PPC64_GNU_VTINHERIT:
case R_PPC64_GNU_VTENTRY:
- continue;
+ case R_PPC64_ENTRY:
+ goto copy_reloc;
/* GOT16 relocations. Like an ADDR16 using the symbol's
address in the GOT as relocation value instead of the
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)
+ || UNDEFWEAK_NO_DYNAMIC_RELOC (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
;
else
{
- BFD_ASSERT (h->elf.dynindx != -1);
indx = h->elf.dynindx;
unresolved_reloc = FALSE;
}
? h->elf.type == STT_GNU_IFUNC
: 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))
+ {
+ relgot = htab->elf.irelplt;
+ if (indx == 0)
+ htab->local_ifunc_resolver = 1;
+ else if (is_static_defined (&h->elf))
+ htab->maybe_local_ifunc_resolver = 1;
+ }
+ else if (indx != 0
+ || (bfd_link_pic (info)
+ && (h == NULL
+ || !UNDEFWEAK_NO_DYNAMIC_RELOC (info, &h->elf)
+ || (tls_type == (TLS_TLS | TLS_LD)
+ && !h->elf.def_dynamic))))
relgot = ppc64_elf_tdata (ent->owner)->relgot;
if (relgot != NULL)
{
else
{
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)
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;
}
}
-
bfd_put_64 (output_bfd, relocation,
got->contents + off);
}
case R_PPC64_PLT64:
/* Relocation is to the entry for this symbol in the
procedure linkage table. */
+ {
+ struct plt_entry **plt_list = NULL;
+ if (h != NULL)
+ plt_list = &h->elf.plt.plist;
+ else if (local_got_ents != NULL)
+ {
+ struct plt_entry **local_plt = (struct plt_entry **)
+ (local_got_ents + symtab_hdr->sh_info);
+ unsigned char *local_got_tls_masks = (unsigned char *)
+ (local_plt + symtab_hdr->sh_info);
+ if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
+ plt_list = local_plt + r_symndx;
+ }
+ if (plt_list)
+ {
+ struct plt_entry *ent;
- /* Resolve a PLT reloc against a local symbol directly,
- without using the procedure linkage table. */
- if (h == NULL)
- break;
-
- /* It's possible that we didn't make a PLT entry for this
- symbol. This happens when statically linking PIC code,
- or when using -Bsymbolic. Go find a match if there is a
- PLT entry. */
- if (htab->elf.splt != NULL)
- {
- struct plt_entry *ent;
- for (ent = h->elf.plt.plist; ent != NULL; ent = ent->next)
- if (ent->plt.offset != (bfd_vma) -1
- && ent->addend == orig_rel.r_addend)
- {
- relocation = (htab->elf.splt->output_section->vma
- + htab->elf.splt->output_offset
- + ent->plt.offset);
- unresolved_reloc = FALSE;
- break;
- }
- }
+ for (ent = *plt_list; ent != NULL; ent = ent->next)
+ if (ent->plt.offset != (bfd_vma) -1
+ && ent->addend == orig_rel.r_addend)
+ {
+ asection *plt;
+
+ plt = htab->elf.splt;
+ if (!htab->elf.dynamic_sections_created
+ || h == NULL
+ || h->elf.dynindx == -1)
+ plt = htab->elf.iplt;
+ relocation = (plt->output_section->vma
+ + plt->output_offset
+ + ent->plt.offset);
+ addend = 0;
+ unresolved_reloc = FALSE;
+ break;
+ }
+ }
+ }
break;
case R_PPC64_TOC:
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HI:
case R_PPC64_REL16_HA:
+ case R_PPC64_REL16DX_HA:
break;
case R_PPC64_REL14:
defined before using them. */
bfd_byte *p = contents + rel->r_offset - d_offset;
- insn = bfd_get_32 (output_bfd, p);
+ insn = bfd_get_32 (input_bfd, p);
insn = _bfd_elf_ppc_at_tprel_transform (insn, 13);
if (insn != 0)
- bfd_put_32 (output_bfd, insn, p);
+ bfd_put_32 (input_bfd, insn, p);
break;
}
if (htab->elf.tls_sec != NULL)
addend -= htab->elf.tls_sec->vma + TP_OFFSET;
- if (bfd_link_pic (info))
- /* The TPREL16 relocs shouldn't really be used in shared
- libs as they will result in DT_TEXTREL being set, but
- support them anyway. */
- goto dodyn;
- break;
+ /* The TPREL16 relocs shouldn't really be used in shared
+ libs or with non-local symbols as that will result in
+ DT_TEXTREL being set, but support them anyway. */
+ goto dodyn;
case R_PPC64_DTPREL16:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL64:
if (htab->elf.tls_sec != NULL)
addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
- /* Fall thru */
+ /* Fall through. */
/* Relocations that may need to be propagated if this is a
dynamic object. */
if (NO_OPD_RELOCS && is_opd)
break;
- if ((bfd_link_pic (info)
- && (h == NULL
- || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT
- || h->elf.root.type != bfd_link_hash_undefweak)
- && (must_be_dyn_reloc (info, r_type)
- || !SYMBOL_CALLS_LOCAL (info, &h->elf)))
- || (ELIMINATE_COPY_RELOCS
- && !bfd_link_pic (info)
- && h != NULL
- && h->elf.dynindx != -1
- && !h->elf.non_got_ref
- && !h->elf.def_regular)
- || (!bfd_link_pic (info)
- && (h != NULL
- ? h->elf.type == STT_GNU_IFUNC
- : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)))
+ if (bfd_link_pic (info)
+ ? ((h == NULL
+ || h->dyn_relocs != NULL)
+ && ((h != NULL && pc_dynrelocs (h))
+ || must_be_dyn_reloc (info, r_type)))
+ : (h != NULL
+ ? h->dyn_relocs != NULL
+ : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
{
bfd_boolean skip, relocate;
asection *sreloc;
bfd_vma out_off;
+ long indx = 0;
/* When generating a dynamic object, these relocations
are copied into the output file to be resolved at run
&& !is_opd
&& r_type != R_PPC64_TOC)
{
- BFD_ASSERT (h->elf.dynindx != -1);
- outrel.r_info = ELF64_R_INFO (h->elf.dynindx, r_type);
+ indx = h->elf.dynindx;
+ BFD_ASSERT (indx != -1);
+ outrel.r_info = ELF64_R_INFO (indx, r_type);
}
else
{
}
else
{
- long indx = 0;
-
if (h != NULL
? h->elf.type == STT_GNU_IFUNC
: ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
{
info->callbacks->einfo
- (_("%P: %H: %s for indirect "
+ /* xgettext:c-format */
+ (_("%H: %s for indirect "
"function `%T' unsupported\n"),
input_bfd, input_section, rel->r_offset,
ppc64_elf_howto_table[r_type]->name,
if (h != NULL
? h->elf.type == STT_GNU_IFUNC
: ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
- sreloc = htab->elf.irelplt;
+ {
+ sreloc = htab->elf.irelplt;
+ if (indx == 0)
+ htab->local_ifunc_resolver = 1;
+ else if (is_static_defined (&h->elf))
+ htab->maybe_local_ifunc_resolver = 1;
+ }
if (sreloc == NULL)
abort ();
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;
}
}
break;
/* These ones haven't been implemented yet. */
info->callbacks->einfo
+ /* xgettext:c-format */
(_("%P: %B: %s is not supported for `%T'\n"),
input_bfd,
ppc64_elf_howto_table[r_type]->name, sym_name);
bfd_set_error (bfd_error_invalid_operation);
ret = FALSE;
- continue;
+ goto copy_reloc;
}
/* Multi-instruction sequences that access the TOC can be
optimized, eg. addis ra,r2,0; addi rb,ra,x;
to nop; addi rb,r2,x; */
+ howto = ppc64_elf_howto_table[(int) r_type];
switch (r_type)
{
default:
bfd_put_32 (input_bfd, insn, p);
}
break;
+
+ case R_PPC64_TPREL16_HA:
+ if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000)
+ {
+ bfd_byte *p = contents + (rel->r_offset & ~3);
+ insn = bfd_get_32 (input_bfd, p);
+ if ((insn & ((0x3f << 26) | 0x1f << 16))
+ != ((15u << 26) | (13 << 16)) /* addis rt,13,imm */)
+ /* xgettext:c-format */
+ info->callbacks->minfo
+ (_("%H: warning: %s unexpected insn %#x.\n"),
+ input_bfd, input_section, rel->r_offset, howto->name, insn);
+ else
+ bfd_put_32 (input_bfd, NOP, p);
+ }
+ break;
+
+ case R_PPC64_TPREL16_LO:
+ case R_PPC64_TPREL16_LO_DS:
+ if (htab->do_tls_opt && relocation + addend + 0x8000 < 0x10000)
+ {
+ bfd_byte *p = contents + (rel->r_offset & ~3);
+ insn = bfd_get_32 (input_bfd, p);
+ insn &= ~(0x1f << 16);
+ insn |= 13 << 16;
+ bfd_put_32 (input_bfd, insn, p);
+ }
+ break;
}
/* Do any further special processing. */
- howto = ppc64_elf_howto_table[(int) r_type];
switch (r_type)
{
default:
break;
case R_PPC64_REL16_HA:
+ case R_PPC64_REL16DX_HA:
case R_PPC64_ADDR16_HA:
case R_PPC64_ADDR16_HIGHA:
case R_PPC64_ADDR16_HIGHERA:
alone (it will be set to zero elsewhere in the link). */
if (sec == NULL)
break;
- /* Fall thru */
+ /* Fall through. */
case R_PPC64_GOT16_HA:
case R_PPC64_PLTGOT16_HA:
case R_PPC64_DTPREL16_LO_DS:
insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
mask = 3;
- /* If this reloc is against an lq insn, then the value must be
- a multiple of 16. This is somewhat of a hack, but the
- "correct" way to do this by defining _DQ forms of all the
- _DS relocs bloats all reloc switches in this file. It
- doesn't seem to make much sense to use any of these relocs
- in data, so testing the insn should be safe. */
- if ((insn & (0x3f << 26)) == (56u << 26))
+ /* If this reloc is against an lq, lxv, or stxv insn, then
+ the value must be a multiple of 16. This is somewhat of
+ a hack, but the "correct" way to do this by defining _DQ
+ forms of all the _DS relocs bloats all reloc switches in
+ this file. It doesn't make much sense to use these
+ relocs in data, so testing the insn should be safe. */
+ if ((insn & (0x3f << 26)) == (56u << 26)
+ || ((insn & (0x3f << 26)) == (61u << 26) && (insn & 3) == 1))
mask = 15;
- if (((relocation + addend) & mask) != 0)
+ relocation += addend;
+ addend = insn & (mask ^ 3);
+ if ((relocation & mask) != 0)
{
+ relocation ^= relocation & mask;
info->callbacks->einfo
- (_("%P: %H: error: %s not a multiple of %u\n"),
+ /* xgettext:c-format */
+ (_("%H: error: %s not a multiple of %u\n"),
input_bfd, input_section, rel->r_offset,
howto->name,
mask + 1);
bfd_set_error (bfd_error_bad_value);
ret = FALSE;
- continue;
+ goto copy_reloc;
}
break;
}
rel->r_offset) != (bfd_vma) -1)
{
info->callbacks->einfo
- (_("%P: %H: unresolvable %s against `%T'\n"),
+ /* xgettext:c-format */
+ (_("%H: unresolvable %s against `%T'\n"),
input_bfd, input_section, rel->r_offset,
howto->name,
h->elf.root.root.string);
}
}
- r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
- rel->r_offset, relocation, addend);
+ if (r_type == R_PPC64_REL16DX_HA)
+ {
+ /* Split field reloc isn't handled by _bfd_final_link_relocate. */
+ if (rel->r_offset + 4 > input_section->size)
+ r = bfd_reloc_outofrange;
+ else
+ {
+ relocation += addend;
+ relocation -= (rel->r_offset
+ + input_section->output_offset
+ + input_section->output_section->vma);
+ relocation = (bfd_signed_vma) relocation >> 16;
+ insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ insn &= ~0x1fffc1;
+ insn |= (relocation & 0xffc1) | ((relocation & 0x3e) << 15);
+ bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+ r = bfd_reloc_ok;
+ if (relocation + 0x8000 > 0xffff)
+ r = bfd_reloc_overflow;
+ }
+ }
+ else
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
+ rel->r_offset, relocation, addend);
if (r != bfd_reloc_ok)
{
&& (h->elf.root.type == bfd_link_hash_undefweak
|| h->elf.root.type == bfd_link_hash_undefined)
&& is_branch_reloc (r_type))))
- {
- if (!((*info->callbacks->reloc_overflow)
- (info, &h->elf.root, sym_name,
- reloc_name, orig_rel.r_addend,
- input_bfd, input_section, rel->r_offset)))
- return FALSE;
- }
+ info->callbacks->reloc_overflow (info, &h->elf.root,
+ sym_name, reloc_name,
+ orig_rel.r_addend,
+ input_bfd, input_section,
+ rel->r_offset);
}
else
{
info->callbacks->einfo
- (_("%P: %H: %s against `%T': error %d\n"),
+ /* xgettext:c-format */
+ (_("%H: %s against `%T': error %d\n"),
input_bfd, input_section, rel->r_offset,
reloc_name, sym_name, (int) r);
ret = FALSE;
if (more_info != NULL)
free (more_info);
}
+ copy_reloc:
+ if (wrel != rel)
+ *wrel = *rel;
+ }
+
+ if (wrel != rel)
+ {
+ Elf_Internal_Shdr *rel_hdr;
+ size_t deleted = rel - wrel;
+
+ rel_hdr = _bfd_elf_single_rel_hdr (input_section->output_section);
+ rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+ if (rel_hdr->sh_size == 0)
+ {
+ /* It is too late to remove an empty reloc section. Leave
+ one NONE reloc.
+ ??? What is wrong with an empty section??? */
+ rel_hdr->sh_size = rel_hdr->sh_entsize;
+ deleted -= 1;
+ }
+ rel_hdr = _bfd_elf_single_rel_hdr (input_section);
+ rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+ input_section->reloc_count -= deleted;
}
/* If we're emitting relocations, then shortly after this function
ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
struct bfd_link_info *info,
struct elf_link_hash_entry *h,
- Elf_Internal_Sym *sym ATTRIBUTE_UNUSED)
+ Elf_Internal_Sym *sym)
{
struct ppc_link_hash_table *htab;
struct plt_entry *ent;
loc = (htab->elf.irelplt->contents
+ (htab->elf.irelplt->reloc_count++
* sizeof (Elf64_External_Rela)));
+ htab->local_ifunc_resolver = 1;
}
else
{
loc = (htab->elf.srelplt->contents
+ ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
/ PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
+ if (h->type == STT_GNU_IFUNC && is_static_defined (h))
+ htab->maybe_local_ifunc_resolver = 1;
}
bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
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 == htab->elf.sdynrelro)
+ 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);
}
case DT_PPC64_OPT:
if (htab->do_multi_toc && htab->multi_toc_needed)
dyn.d_un.d_val |= PPC64_OPT_MULTI_TOC;
+ if (htab->has_plt_localentry0)
+ dyn.d_un.d_val |= PPC64_OPT_LOCALENTRY;
break;
case DT_PPC64_OPDSZ:
dyn.d_un.d_val = htab->elf.srelplt->size;
break;
- case DT_RELASZ:
- /* Don't count procedure linkage table relocs in the
- overall reloc count. */
- s = htab->elf.srelplt;
- if (s == NULL)
- continue;
- dyn.d_un.d_val -= s->size;
- break;
-
- case DT_RELA:
- /* We may not be using the standard ELF linker script.
- If .rela.plt is the first .rela section, we adjust
- DT_RELA to not include it. */
- s = htab->elf.srelplt;
- if (s == NULL)
- continue;
- if (dyn.d_un.d_ptr != s->output_section->vma + s->output_offset)
- continue;
- dyn.d_un.d_ptr += s->size;
- break;
+ case DT_TEXTREL:
+ if (htab->local_ifunc_resolver)
+ info->callbacks->einfo
+ (_("%X%P: text relocations and GNU indirect "
+ "functions will result in a segfault at runtime\n"));
+ else if (htab->maybe_local_ifunc_resolver)
+ info->callbacks->einfo
+ (_("%P: warning: text relocations and GNU indirect "
+ "functions may result in a segfault at runtime\n"));
+ continue;
}
bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
}
}
- 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
{
bfd_vma val;
bfd_byte *p;
- asection *stub_sec;
+ struct map_stub *group;
+ size_t align = 4;
- p = htab->glink_eh_frame->contents + sizeof (glink_eh_frame_cie);
- for (stub_sec = htab->params->stub_bfd->sections;
- stub_sec != NULL;
- stub_sec = stub_sec->next)
- if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+ p = htab->glink_eh_frame->contents;
+ p += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->stub_sec != NULL)
{
- /* FDE length. */
- p += 4;
- /* CIE pointer. */
- p += 4;
/* Offset to stub section. */
- val = (stub_sec->output_section->vma
- + stub_sec->output_offset);
+ val = (group->stub_sec->output_section->vma
+ + group->stub_sec->output_offset);
val -= (htab->glink_eh_frame->output_section->vma
+ htab->glink_eh_frame->output_offset
- + (p - htab->glink_eh_frame->contents));
+ + (p + 8 - 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);
+ group->stub_sec->name);
return FALSE;
}
- bfd_put_32 (dynobj, val, p);
- p += 4;
- /* stub section size. */
- p += 4;
- /* Augmentation. */
- p += 1;
- /* Pad. */
- p += 7;
+ bfd_put_32 (dynobj, val, p + 8);
+ p += stub_eh_frame_size (group, align);
}
if (htab->glink != NULL && htab->glink->size != 0)
{
- /* FDE length. */
- p += 4;
- /* CIE pointer. */
- 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
- + (p - htab->glink_eh_frame->contents));
+ + (p + 8 - htab->glink_eh_frame->contents));
if (val + 0x80000000 > 0xffffffff)
{
info->callbacks->einfo
htab->glink->name);
return FALSE;
}
- bfd_put_32 (dynobj, val, p);
- p += 4;
- /* .glink size. */
- p += 4;
- /* Augmentation. */
- p += 1;
- /* Ops. */
- p += 7;
+ bfd_put_32 (dynobj, val, p + 8);
+ p += (24 + align - 1) & -align;
}
if (htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
#define elf64_bed elf64_powerpc_fbsd_bed
#include "elf64-target.h"
-