#define elf_backend_finish_dynamic_sections ppc64_elf_finish_dynamic_sections
#define elf_backend_link_output_symbol_hook ppc64_elf_output_symbol_hook
#define elf_backend_special_sections ppc64_elf_special_sections
+#define elf_backend_section_flags ppc64_elf_section_flags
#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
if (!sec->used_by_bfd)
{
struct _ppc64_elf_section_data *sdata;
- bfd_size_type amt = sizeof (*sdata);
+ size_t amt = sizeof (*sdata);
sdata = bfd_zalloc (abfd, amt);
if (sdata == NULL)
return _bfd_elf_new_section_hook (abfd, sec);
}
+static bfd_boolean
+ppc64_elf_section_flags (const Elf_Internal_Shdr *hdr)
+{
+ const char *name = hdr->bfd_section->name;
+
+ if (strncmp (name, ".sbss", 5) == 0
+ || strncmp (name, ".sdata", 6) == 0)
+ hdr->bfd_section->flags |= SEC_SMALL_DATA;
+
+ return TRUE;
+}
+
static struct _opd_sec_data *
get_opd_info (asection * sec)
{
ppc_stub_plt_branch:
Similar to the above, but a 24 bit branch in the stub section won't
reach its destination.
- . addis %r11,%r2,xxx@toc@ha
- . ld %r12,xxx@toc@l(%r11)
+ . addis %r12,%r2,xxx@toc@ha
+ . ld %r12,xxx@toc@l(%r12)
. mtctr %r12
. bctr
A ppc_stub_plt_branch with an r2 offset looks like:
. std %r2,40(%r1)
- . addis %r11,%r2,xxx@toc@ha
- . ld %r12,xxx@toc@l(%r11)
+ . addis %r12,%r2,xxx@toc@ha
+ . ld %r12,xxx@toc@l(%r12)
. addis %r2,%r2,off@ha
. addi %r2,%r2,off@l
. mtctr %r12
struct ppc_link_hash_entry *tls_get_addr_fd;
struct ppc_link_hash_entry *tga_desc;
struct ppc_link_hash_entry *tga_desc_fd;
+ struct map_stub *tga_group;
/* The size of reliplt used by got entry relocs. */
bfd_size_type got_reli_size;
ppc64_elf_link_hash_table_create (bfd *abfd)
{
struct ppc_link_hash_table *htab;
- bfd_size_type amt = sizeof (struct ppc_link_hash_table);
+ size_t amt = sizeof (struct ppc_link_hash_table);
htab = bfd_zmalloc (amt);
if (htab == NULL)
memcpy (dot_name + 1, name, len + 1);
h = _bfd_elf_archive_symbol_lookup (abfd, info, dot_name);
bfd_release (abfd, dot_name);
+ if (h != NULL)
+ return h;
+
+ if (strcmp (name, "__tls_get_addr_opt") == 0)
+ h = _bfd_elf_archive_symbol_lookup (abfd, info, "__tls_get_addr_desc");
return h;
}
break;
if (ent == NULL)
{
- bfd_size_type amt = sizeof (*ent);
+ size_t amt = sizeof (*ent);
ent = bfd_alloc (abfd, amt);
if (ent == NULL)
return FALSE;
break;
if (ent == NULL)
{
- bfd_size_type amt = sizeof (*ent);
+ size_t amt = sizeof (*ent);
ent = bfd_alloc (abfd, amt);
if (ent == NULL)
return FALSE;
break;
if (ent == NULL)
{
- bfd_size_type amt = sizeof (*ent);
+ size_t amt = sizeof (*ent);
ent = bfd_alloc (abfd, amt);
if (ent == NULL)
return FALSE;
if (!update_local_sym_info (abfd, symtab_hdr, r_symndx,
rel->r_addend, tls_type))
return FALSE;
-
- /* We may also need a plt entry if the symbol turns out to be
- an ifunc. */
- if (h != NULL && !bfd_link_pic (info) && abiversion (abfd) != 1)
- {
- if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend))
- return FALSE;
- }
break;
case R_PPC64_PLT16_HA:
return FALSE;
relend = relstart + sec->reloc_count;
- for (rel = relstart; rel < relend; )
+ for (rel = relstart; rel < relend; rel++)
{
enum elf_ppc64_reloc_type r_type;
unsigned long r_symndx;
|| discarded_section (sym_sec))
continue;
+ if ((h ? h->type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+ continue;
+
if (!SYMBOL_REFERENCES_LOCAL (info, h))
continue;
stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
info = in_arg;
+ /* Fail if the target section could not be assigned to an output
+ section. The user should fix his linker script. */
+ if (stub_entry->target_section != NULL
+ && stub_entry->target_section->output_section == NULL
+ && info->non_contiguous_regions)
+ info->callbacks->einfo (_("%F%P: Could not assign '%pA' to an output section. "
+ "Retry without --enable-non-contiguous-regions.\n"),
+ stub_entry->target_section);
+
+ /* Same for the group. */
+ if (stub_entry->group->stub_sec != NULL
+ && stub_entry->group->stub_sec->output_section == NULL
+ && info->non_contiguous_regions)
+ info->callbacks->einfo (_("%F%P: Could not assign group %pA target %pA to an "
+ "output section. Retry without "
+ "--enable-non-contiguous-regions.\n"),
+ stub_entry->group->stub_sec,
+ stub_entry->target_section);
+
htab = ppc_hash_table (info);
if (htab == NULL)
return FALSE;
if (htab == NULL)
return FALSE;
+ /* Fail if the target section could not be assigned to an output
+ section. The user should fix his linker script. */
+ if (stub_entry->target_section != NULL
+ && stub_entry->target_section->output_section == NULL
+ && info->non_contiguous_regions)
+ info->callbacks->einfo (_("%F%P: Could not assign %pA to an output section. "
+ "Retry without --enable-non-contiguous-regions.\n"),
+ stub_entry->target_section);
+
+ /* Same for the group. */
+ if (stub_entry->group->stub_sec != NULL
+ && stub_entry->group->stub_sec->output_section == NULL
+ && info->non_contiguous_regions)
+ info->callbacks->einfo (_("%F%P: Could not assign group %pA target %pA to an "
+ "output section. Retry without "
+ "--enable-non-contiguous-regions.\n"),
+ stub_entry->group->stub_sec,
+ stub_entry->target_section);
+
/* Make a note of the offset within the stubs for this entry. */
stub_entry->stub_offset = stub_entry->group->stub_sec->size;
ppc64_elf_setup_section_lists (struct bfd_link_info *info)
{
unsigned int id;
- bfd_size_type amt;
+ size_t amt;
struct ppc_link_hash_table *htab = ppc_hash_table (info);
if (htab == NULL)
if (!group_sections (info, stub_group_size, stubs_always_before_branch))
return FALSE;
+ htab->tga_group = NULL;
+ if (!htab->params->no_tls_get_addr_regsave
+ && htab->tga_desc_fd != NULL
+ && (htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefined
+ || htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefweak)
+ && htab->tls_get_addr_fd != NULL
+ && is_static_defined (&htab->tls_get_addr_fd->elf))
+ {
+ asection *sym_sec, *code_sec, *stub_sec;
+ bfd_vma sym_value;
+ struct _opd_sec_data *opd;
+
+ sym_sec = htab->tls_get_addr_fd->elf.root.u.def.section;
+ sym_value = defined_sym_val (&htab->tls_get_addr_fd->elf);
+ code_sec = sym_sec;
+ opd = get_opd_info (sym_sec);
+ if (opd != NULL)
+ opd_entry_value (sym_sec, sym_value, &code_sec, NULL, FALSE);
+ htab->tga_group = htab->sec_info[code_sec->id].u.group;
+ stub_sec = (*htab->params->add_stub_section) (".tga_desc.stub",
+ htab->tga_group->link_sec);
+ if (stub_sec == NULL)
+ return FALSE;
+ htab->tga_group->stub_sec = stub_sec;
+
+ htab->tga_desc_fd->elf.root.type = bfd_link_hash_defined;
+ htab->tga_desc_fd->elf.root.u.def.section = stub_sec;
+ htab->tga_desc_fd->elf.root.u.def.value = 0;
+ htab->tga_desc_fd->elf.type = STT_FUNC;
+ htab->tga_desc_fd->elf.def_regular = 1;
+ htab->tga_desc_fd->elf.non_elf = 0;
+ _bfd_elf_link_hash_hide_symbol (info, &htab->tga_desc_fd->elf, TRUE);
+ }
+
#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
stub_sec->flags &= ~SEC_RELOC;
}
}
+ if (htab->tga_group != NULL)
+ {
+ /* See emit_tga_desc and emit_tga_desc_eh_frame. */
+ htab->tga_group->eh_size
+ = 1 + 2 + (htab->opd_abi != 0) + 3 + 8 * 2 + 3 + 8 + 3;
+ htab->tga_group->lr_restore = 23 * 4;
+ htab->tga_group->stub_sec->size = 24 * 4;
+ }
if (htab->stub_iteration <= STUB_SHRINK_ITER
|| htab->brlt->rawsize < htab->brlt->size)
|| (htab->stub_iteration > STUB_SHRINK_ITER
&& htab->brlt->rawsize > htab->brlt->size))
&& (htab->glink_eh_frame == NULL
- || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size))
+ || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size)
+ && (htab->tga_group == NULL
+ || htab->stub_iteration > 1))
break;
/* Ask the linker to do its stuff. */
}
maybe_strip_output (info, htab->brlt);
+ if (htab->relbrlt != NULL)
+ maybe_strip_output (info, htab->relbrlt);
if (htab->glink_eh_frame != NULL)
maybe_strip_output (info, htab->glink_eh_frame);
return TRUE;
}
+/* Emit the static wrapper function preserving registers around a
+ __tls_get_addr_opt call. */
+
+static bfd_boolean
+emit_tga_desc (struct ppc_link_hash_table *htab)
+{
+ asection *stub_sec = htab->tga_group->stub_sec;
+ unsigned int cfa_updt = 11 * 4;
+ bfd_byte *p;
+ bfd_vma to, from, delta;
+
+ BFD_ASSERT (htab->tga_desc_fd->elf.root.type == bfd_link_hash_defined
+ && htab->tga_desc_fd->elf.root.u.def.section == stub_sec
+ && htab->tga_desc_fd->elf.root.u.def.value == 0);
+ to = defined_sym_val (&htab->tls_get_addr_fd->elf);
+ from = defined_sym_val (&htab->tga_desc_fd->elf) + cfa_updt;
+ delta = to - from;
+ if (delta + (1 << 25) >= 1 << 26)
+ {
+ _bfd_error_handler (_("__tls_get_addr call offset overflow"));
+ htab->stub_error = TRUE;
+ return FALSE;
+ }
+
+ p = stub_sec->contents;
+ p = tls_get_addr_prologue (htab->elf.dynobj, p, htab);
+ bfd_put_32 (stub_sec->owner, B_DOT | 1 | (delta & 0x3fffffc), p);
+ p += 4;
+ p = tls_get_addr_epilogue (htab->elf.dynobj, p, htab);
+ return stub_sec->size == (bfd_size_type) (p - stub_sec->contents);
+}
+
+/* Emit eh_frame describing the static wrapper function. */
+
+static bfd_byte *
+emit_tga_desc_eh_frame (struct ppc_link_hash_table *htab, bfd_byte *p)
+{
+ unsigned int cfa_updt = 11 * 4;
+ unsigned int i;
+
+ *p++ = DW_CFA_advance_loc + cfa_updt / 4;
+ *p++ = DW_CFA_def_cfa_offset;
+ if (htab->opd_abi)
+ {
+ *p++ = 128;
+ *p++ = 1;
+ }
+ else
+ *p++ = 96;
+ *p++ = DW_CFA_offset_extended_sf;
+ *p++ = 65;
+ *p++ = (-16 / 8) & 0x7f;
+ for (i = 4; i < 12; i++)
+ {
+ *p++ = DW_CFA_offset + i;
+ *p++ = (htab->opd_abi ? 13 : 12) - i;
+ }
+ *p++ = DW_CFA_advance_loc + 10;
+ *p++ = DW_CFA_def_cfa_offset;
+ *p++ = 0;
+ for (i = 4; i < 12; i++)
+ *p++ = DW_CFA_restore + i;
+ *p++ = DW_CFA_advance_loc + 2;
+ *p++ = DW_CFA_restore_extended;
+ *p++ = 65;
+ return p;
+}
+
/* Build all the stubs associated with the current output file.
The stubs are kept in a hash table attached to the main linker
hash table. This function is called via gldelf64ppc_finish. */
}
}
+ if (htab->tga_group != NULL)
+ {
+ htab->tga_group->lr_restore = 23 * 4;
+ htab->tga_group->stub_sec->size = 24 * 4;
+ if (!emit_tga_desc (htab))
+ return FALSE;
+ if (htab->glink_eh_frame != NULL
+ && htab->glink_eh_frame->size != 0)
+ {
+ size_t align = 4;
+
+ p = htab->glink_eh_frame->contents;
+ p += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+ p += 17;
+ htab->tga_group->eh_size = emit_tga_desc_eh_frame (htab, p) - p;
+ }
+ }
+
/* Build .glink global entry stubs, and PLT relocs for globals. */
elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info);
if (stats != NULL)
{
- size_t len;
- *stats = bfd_malloc (500);
- if (*stats == NULL)
- return FALSE;
-
- len = sprintf (*stats,
- ngettext ("linker stubs in %u group\n",
- "linker stubs in %u groups\n",
- stub_sec_count),
- stub_sec_count);
- sprintf (*stats + len, _(" branch %lu\n"
- " branch toc adj %lu\n"
- " branch notoc %lu\n"
- " branch both %lu\n"
- " long branch %lu\n"
- " long toc adj %lu\n"
- " long notoc %lu\n"
- " long both %lu\n"
- " plt call %lu\n"
- " plt call save %lu\n"
- " plt call notoc %lu\n"
- " plt call both %lu\n"
- " global entry %lu"),
- htab->stub_count[ppc_stub_long_branch - 1],
- htab->stub_count[ppc_stub_long_branch_r2off - 1],
- htab->stub_count[ppc_stub_long_branch_notoc - 1],
- htab->stub_count[ppc_stub_long_branch_both - 1],
- htab->stub_count[ppc_stub_plt_branch - 1],
- htab->stub_count[ppc_stub_plt_branch_r2off - 1],
- htab->stub_count[ppc_stub_plt_branch_notoc - 1],
- htab->stub_count[ppc_stub_plt_branch_both - 1],
- htab->stub_count[ppc_stub_plt_call - 1],
- htab->stub_count[ppc_stub_plt_call_r2save - 1],
- htab->stub_count[ppc_stub_plt_call_notoc - 1],
- htab->stub_count[ppc_stub_plt_call_both - 1],
- htab->stub_count[ppc_stub_global_entry - 1]);
+ char *groupmsg;
+ if (asprintf (&groupmsg,
+ ngettext ("linker stubs in %u group\n",
+ "linker stubs in %u groups\n",
+ stub_sec_count),
+ stub_sec_count) < 0)
+ *stats = NULL;
+ else
+ {
+ if (asprintf (stats, _("%s"
+ " branch %lu\n"
+ " branch toc adj %lu\n"
+ " branch notoc %lu\n"
+ " branch both %lu\n"
+ " long branch %lu\n"
+ " long toc adj %lu\n"
+ " long notoc %lu\n"
+ " long both %lu\n"
+ " plt call %lu\n"
+ " plt call save %lu\n"
+ " plt call notoc %lu\n"
+ " plt call both %lu\n"
+ " global entry %lu"),
+ groupmsg,
+ htab->stub_count[ppc_stub_long_branch - 1],
+ htab->stub_count[ppc_stub_long_branch_r2off - 1],
+ htab->stub_count[ppc_stub_long_branch_notoc - 1],
+ htab->stub_count[ppc_stub_long_branch_both - 1],
+ htab->stub_count[ppc_stub_plt_branch - 1],
+ htab->stub_count[ppc_stub_plt_branch_r2off - 1],
+ htab->stub_count[ppc_stub_plt_branch_notoc - 1],
+ htab->stub_count[ppc_stub_plt_branch_both - 1],
+ htab->stub_count[ppc_stub_plt_call - 1],
+ htab->stub_count[ppc_stub_plt_call_r2save - 1],
+ htab->stub_count[ppc_stub_plt_call_notoc - 1],
+ htab->stub_count[ppc_stub_plt_call_both - 1],
+ htab->stub_count[ppc_stub_global_entry - 1]) < 0)
+ *stats = NULL;
+ free (groupmsg);
+ }
}
return TRUE;
}
break;
case R_PPC64_GOT16_DS:
+ if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+ break;
from = TOCstart + htab->sec_info[input_section->id].toc_off;
if (relocation + addend - from + 0x8000 < 0x10000
&& SYMBOL_REFERENCES_LOCAL (info, &h->elf))
case R_PPC64_GOT16_LO_DS:
case R_PPC64_GOT16_HA:
+ if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+ break;
from = TOCstart + htab->sec_info[input_section->id].toc_off;
if (relocation + addend - from + 0x80008000ULL < 0x100000000ULL
&& SYMBOL_REFERENCES_LOCAL (info, &h->elf))
break;
case R_PPC64_GOT_PCREL34:
+ if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+ break;
from = (rel->r_offset
+ input_section->output_section->vma
+ input_section->output_offset);