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 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 {
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.
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;
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;
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));
}
static bfd_boolean
ppc64_elf_merge_symbol (struct elf_link_hash_entry *h,
- const Elf_Internal_Sym *isym ATTRIBUTE_UNUSED,
+ const Elf_Internal_Sym *isym,
asection **psec ATTRIBUTE_UNUSED,
bfd_boolean newdef ATTRIBUTE_UNUSED,
bfd_boolean olddef 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;
}
/* Propagate reference flags from entry symbol to function
descriptor symbol. */
- fdh->elf.root.non_ir_ref |= eh->elf.root.non_ir_ref;
+ 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;
/* 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 = 1;
+ eh->oh->elf.root.non_ir_ref_regular = 1;
if (h == htab->elf.hgot)
sec->has_toc_reloc = 1;
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;
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
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
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:
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));
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
{
/* 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 H is undefined weak, make it dynamic if that makes sense. */
+/* If H is undefined, make it dynamic if that makes sense. */
static bfd_boolean
-ensure_undefweak_dynamic (struct bfd_link_info *info,
- struct elf_link_hash_entry *h)
+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
+ && ((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)
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. */
- if (!ensure_undefweak_dynamic (info, h))
+ /* Make sure this symbol is output as a dynamic symbol. */
+ if (!ensure_undef_dynamic (info, h))
return FALSE;
if (!is_ppc64_elf (gent->owner))
if (eh->dyn_relocs != NULL)
{
- /* Make sure this symbol is output as a dynamic symbol.
- Undefined weak syms won't yet be marked as dynamic. */
- if (!ensure_undefweak_dynamic (info, h))
+ /* Make sure this symbol is output as a dynamic symbol. */
+ if (!ensure_undef_dynamic (info, h))
return FALSE;
}
}
if (!h->non_got_ref
&& !h->def_regular)
{
- /* Make sure this symbol is output as a dynamic symbol.
- Undefined weak syms won't yet be marked as dynamic. */
- if (!ensure_undefweak_dynamic (info, h))
+ /* Make sure this symbol is output as a dynamic symbol. */
+ if (!ensure_undef_dynamic (info, h))
return FALSE;
if (h->dynindx == -1)
&& (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);
- 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)
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
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
bfd *input_bfd;
unsigned int bfd_indx;
struct map_stub *group;
- asection *stub_sec;
htab->stub_iteration += 1;
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)
{
+ 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. */
{
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 += (17 + align - 1) & -align;
+ 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 + align - 1) & -align;
if (size != 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)
- 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
- || stub_sec->rawsize < stub_sec->size))
+ || group->stub_sec->rawsize < group->stub_sec->size))
break;
- 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)
bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
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 = ((17 + align - 1) & -align) - 4;
+ last_fde_len = stub_eh_frame_size (group, align) - 4;
/* FDE length. */
bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
p += 4;
/* 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 += ((17 + align - 1) & -align) - 17;
+ p = last_fde + last_fde_len + 4;
}
if (htab->glink != NULL && htab->glink->size != 0)
{
*p++ = DW_CFA_register;
*p++ = 65;
*p++ = htab->opd_abi ? 12 : 0;
- *p++ = DW_CFA_advance_loc + 4;
+ *p++ = DW_CFA_advance_loc + (htab->opd_abi ? 5 : 7);
*p++ = DW_CFA_restore_extended;
*p++ = 65;
p += ((24 + align - 1) & -align) - 24;
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);
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));
- 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
/* 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))
{
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:
bfd_put_32 (input_bfd, insn1,
contents + rel->r_offset - d_offset);
if (offset != (bfd_vma) -1)
- {
- insn3 = bfd_get_32 (input_bfd,
- contents + offset + 4);
- if (insn3 == NOP
- || insn3 == CROR_151515 || insn3 == CROR_313131)
- {
- rel[1].r_offset += 4;
- bfd_put_32 (input_bfd, insn2, contents + offset + 4);
- insn2 = NOP;
- }
- bfd_put_32 (input_bfd, insn2, contents + offset);
- }
+ bfd_put_32 (input_bfd, insn2, contents + offset);
if ((tls_mask & tls_gd) == 0
&& (tls_gd == 0 || toc_symndx != 0))
{
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 (input_bfd,
- contents + offset + 4);
- if (insn3 == NOP
- || insn3 == CROR_151515 || insn3 == CROR_313131)
- {
- rel->r_offset += 4;
- bfd_put_32 (input_bfd, insn2, contents + offset + 4);
- insn2 = NOP;
- }
bfd_put_32 (input_bfd, insn2, contents + offset);
if ((tls_mask & TLS_TPRELGD) == 0 && toc_symndx != 0)
goto again;
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 (input_bfd,
- contents + offset + 4);
- if (insn3 == NOP
- || insn3 == CROR_151515 || insn3 == CROR_313131)
- {
- rel->r_offset += 4;
- bfd_put_32 (input_bfd, insn2, contents + offset + 4);
- insn2 = NOP;
- }
bfd_put_32 (input_bfd, insn2, contents + offset);
goto again;
}
{
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;
emitting a reloc. */
else
{
- int tlsopt
- = (htab->params->tls_get_addr_opt
- && htab->tls_get_addr_fd != NULL
- && htab->tls_get_addr_fd->elf.plt.plist != NULL);
-
relocation += addend;
if (tls_type != 0)
{
relocation = 0;
else
relocation -= htab->elf.tls_sec->vma + DTP_OFFSET;
- if ((tls_type & TLS_TPREL)
- || (tlsopt && !(tls_type & TLS_DTPREL)))
+ if (tls_type & TLS_TPREL)
relocation += DTP_OFFSET - TP_OFFSET;
}
{
bfd_put_64 (output_bfd, relocation,
got->contents + off + 8);
- relocation = !tlsopt;
+ relocation = 1;
}
}
bfd_put_64 (output_bfd, relocation,
}
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:
addend = outrel.r_offset;
}
}
- else if (r_type == R_PPC64_DTPMOD64
- && htab->params->tls_get_addr_opt
- && htab->tls_get_addr_fd != NULL
- && htab->tls_get_addr_fd->elf.plt.plist != NULL)
- {
- /* Set up for __tls_get_addr_opt stub, when this entry
- does not have dynamic relocs. */
- relocation = 0;
- /* Set up the next word for local dynamic. If it turns
- out to be global dynamic, the reloc will overwrite
- this value. */
- if (rel->r_offset + 16 <= input_section->size)
- bfd_put_64 (input_bfd, DTP_OFFSET - TP_OFFSET,
- contents + rel->r_offset + 8);
- }
- else if (r_type == R_PPC64_DTPREL64
- && htab->params->tls_get_addr_opt
- && htab->tls_get_addr_fd != NULL
- && htab->tls_get_addr_fd->elf.plt.plist != NULL
- && rel > relocs
- && rel[-1].r_info == ELF64_R_INFO (r_symndx, R_PPC64_DTPMOD64)
- && rel[-1].r_offset + 8 == rel->r_offset)
- {
- /* __tls_get_addr_opt stub value. */
- addend += DTP_OFFSET - TP_OFFSET;
- }
break;
case R_PPC64_COPY:
/* 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:
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:
{
bfd_vma val;
bfd_byte *p;
- asection *stub_sec;
+ struct map_stub *group;
size_t align = 4;
p = htab->glink_eh_frame->contents;
p += (sizeof (glink_eh_frame_cie) + align - 1) & -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 (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 += ((17 + align - 1) & -align) - 17;
+ 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;
- p += ((24 + align - 1) & -align) - 24;
+ 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"
-