aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-ppc.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2009-07-10 12:19:58 +0000
committerAlan Modra <amodra@gmail.com>2009-07-10 12:19:58 +0000
commite054468f6cf120142dbfdf64226a5c6c09ecf998 (patch)
tree835b16de7d80522f5f8da90ddcc8bbd59fe58548 /bfd/elf32-ppc.c
parentfbb8f2990ccfa180274ab4a578818fe247823540 (diff)
downloadfsf-binutils-gdb-e054468f6cf120142dbfdf64226a5c6c09ecf998.zip
fsf-binutils-gdb-e054468f6cf120142dbfdf64226a5c6c09ecf998.tar.gz
fsf-binutils-gdb-e054468f6cf120142dbfdf64226a5c6c09ecf998.tar.bz2
STT_GNU_IFUNC support for PowerPC.
Diffstat (limited to 'bfd/elf32-ppc.c')
-rw-r--r--bfd/elf32-ppc.c648
1 files changed, 478 insertions, 170 deletions
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c
index 0a27ffd..814c8a0 100644
--- a/bfd/elf32-ppc.c
+++ b/bfd/elf32-ppc.c
@@ -1382,6 +1382,20 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
0xffff, /* dst_mask */
FALSE), /* pcrel_offset */
+ HOWTO (R_PPC_IRELATIVE, /* type */
+ 0, /* rightshift */
+ 2, /* size (0 = byte, 1 = short, 2 = long) */
+ 32, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC_IRELATIVE", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffffffff, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
/* A 16 bit relative relocation. */
HOWTO (R_PPC_REL16, /* type */
0, /* rightshift */
@@ -2486,7 +2500,11 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
size = count * sizeof (asymbol);
p = relplt->relocation;
for (i = 0; i < count; i++, p++)
- size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+ {
+ size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+ if (p->addend != 0)
+ size += sizeof ("+0x") - 1 + 8;
+ }
size += sizeof (asymbol) + sizeof ("__glink");
@@ -2516,6 +2534,13 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
len = strlen ((*p->sym_ptr_ptr)->name);
memcpy (names, (*p->sym_ptr_ptr)->name, len);
names += len;
+ if (p->addend != 0)
+ {
+ memcpy (names, "+0x", sizeof ("+0x") - 1);
+ names += sizeof ("+0x") - 1;
+ bfd_sprintf_vma (abfd, names, p->addend);
+ names += strlen (names);
+ }
memcpy (names, "@plt", sizeof ("@plt"));
names += sizeof ("@plt");
++s;
@@ -2665,6 +2690,7 @@ struct ppc_elf_link_hash_entry
#define TLS_DTPREL 8 /* DTPREL reloc, => LD. */
#define TLS_TLS 16 /* Any TLS reloc. */
#define TLS_TPRELGD 32 /* TPREL reloc resulting from GD->IE. */
+#define PLT_IFUNC 64 /* STT_GNU_IFUNC. */
char tls_mask;
/* Nonzero if we have seen a small data relocation referring to this
@@ -2686,6 +2712,8 @@ struct ppc_elf_link_hash_table
asection *glink;
asection *plt;
asection *relplt;
+ asection *iplt;
+ asection *reliplt;
asection *dynbss;
asection *relbss;
asection *dynsbss;
@@ -2852,6 +2880,38 @@ ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
return TRUE;
}
+static bfd_boolean
+ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
+{
+ struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+ asection *s;
+ flagword flags;
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags);
+ htab->glink = s;
+ if (s == NULL
+ || !bfd_set_section_alignment (abfd, s, 4))
+ return FALSE;
+
+ flags = SEC_ALLOC | SEC_LINKER_CREATED;
+ s = bfd_make_section_anyway_with_flags (abfd, ".iplt", flags);
+ htab->iplt = s;
+ if (s == NULL
+ || !bfd_set_section_alignment (abfd, s, 4))
+ return FALSE;
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags);
+ htab->reliplt = s;
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s, 2))
+ return FALSE;
+ return TRUE;
+}
+
/* We have to create .dynsbss and .rela.sbss here so that they get mapped
to output sections (just like _bfd_elf_create_dynamic_sections has
to create .dynbss and .rela.bss). */
@@ -2872,13 +2932,8 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
if (!_bfd_elf_create_dynamic_sections (abfd, info))
return FALSE;
- flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
- | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-
- s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags | SEC_CODE);
- htab->glink = s;
- if (s == NULL
- || !bfd_set_section_alignment (abfd, s, 4))
+ if (htab->glink == NULL
+ && !ppc_elf_create_glink (abfd, info))
return FALSE;
htab->dynbss = bfd_get_section_by_name (abfd, ".dynbss");
@@ -2891,6 +2946,8 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
if (! info->shared)
{
htab->relbss = bfd_get_section_by_name (abfd, ".rela.bss");
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
s = bfd_make_section_with_flags (abfd, ".rela.sbss", flags);
htab->relsbss = s;
if (s == NULL
@@ -3064,6 +3121,9 @@ ppc_elf_add_symbol_hook (bfd *abfd,
*valp = sym->st_size;
}
+ if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+
return TRUE;
}
@@ -3212,30 +3272,35 @@ elf_create_pointer_linker_section (bfd *abfd,
return TRUE;
}
-static bfd_boolean
+static struct plt_entry **
update_local_sym_info (bfd *abfd,
Elf_Internal_Shdr *symtab_hdr,
unsigned long r_symndx,
int tls_type)
{
bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd);
+ struct plt_entry **local_plt;
char *local_got_tls_masks;
if (local_got_refcounts == NULL)
{
bfd_size_type size = symtab_hdr->sh_info;
- size *= sizeof (*local_got_refcounts) + sizeof (*local_got_tls_masks);
+ size *= (sizeof (*local_got_refcounts)
+ + sizeof (*local_plt)
+ + sizeof (*local_got_tls_masks));
local_got_refcounts = bfd_zalloc (abfd, size);
if (local_got_refcounts == NULL)
- return FALSE;
+ return NULL;
elf_local_got_refcounts (abfd) = local_got_refcounts;
}
- local_got_refcounts[r_symndx] += 1;
- local_got_tls_masks = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+ local_plt = (struct plt_entry **) (local_got_refcounts + symtab_hdr->sh_info);
+ local_got_tls_masks = (char *) (local_plt + symtab_hdr->sh_info);
local_got_tls_masks[r_symndx] |= tls_type;
- return TRUE;
+ if (tls_type != PLT_IFUNC)
+ local_got_refcounts[r_symndx] += 1;
+ return local_plt + r_symndx;
}
static bfd_boolean
@@ -3276,6 +3341,21 @@ find_plt_ent (struct plt_entry **plist, asection *sec, bfd_vma addend)
return ent;
}
+static bfd_boolean
+is_branch_reloc (enum elf_ppc_reloc_type r_type)
+{
+ return (r_type == R_PPC_PLTREL24
+ || r_type == R_PPC_LOCAL24PC
+ || r_type == R_PPC_REL24
+ || r_type == R_PPC_REL14
+ || r_type == R_PPC_REL14_BRTAKEN
+ || r_type == R_PPC_REL14_BRNTAKEN
+ || r_type == R_PPC_ADDR24
+ || r_type == R_PPC_ADDR14
+ || r_type == R_PPC_ADDR14_BRTAKEN
+ || r_type == R_PPC_ADDR14_BRNTAKEN);
+}
+
static void
bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type)
{
@@ -3342,6 +3422,7 @@ ppc_elf_check_relocs (bfd *abfd,
enum elf_ppc_reloc_type r_type;
struct elf_link_hash_entry *h;
int tls_type;
+ struct plt_entry **ifunc;
r_symndx = ELF32_R_SYM (rel->r_info);
if (r_symndx < symtab_hdr->sh_info)
@@ -3369,34 +3450,69 @@ ppc_elf_check_relocs (bfd *abfd,
}
tls_type = 0;
+ ifunc = NULL;
r_type = ELF32_R_TYPE (rel->r_info);
- if (h != NULL && h == tga)
- switch (r_type)
- {
- default:
- break;
+ if (!htab->is_vxworks && is_branch_reloc (r_type))
+ {
+ if (h != NULL && h == tga)
+ {
+ if (rel != relocs
+ && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD
+ || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_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;
+ }
- case R_PPC_PLTREL24:
- case R_PPC_LOCAL24PC:
- case R_PPC_REL24:
- case R_PPC_REL14:
- case R_PPC_REL14_BRTAKEN:
- case R_PPC_REL14_BRNTAKEN:
- case R_PPC_ADDR24:
- case R_PPC_ADDR14:
- case R_PPC_ADDR14_BRTAKEN:
- case R_PPC_ADDR14_BRNTAKEN:
- if (rel != relocs
- && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD
- || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_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;
- break;
- }
+ /* STT_GNU_IFUNC symbols must have a PLT entry. */
+ if (h != NULL)
+ {
+ if (h->type == STT_GNU_IFUNC)
+ {
+ h->needs_plt = 1;
+ ifunc = &h->plt.plist;
+ }
+ }
+ else
+ {
+ Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ {
+ ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+ PLT_IFUNC);
+ if (ifunc == NULL)
+ return FALSE;
+ }
+ }
+ if (ifunc != NULL)
+ {
+ bfd_vma addend = 0;
+
+ if (r_type == R_PPC_PLTREL24)
+ {
+ ppc_elf_tdata (abfd)->makes_plt_call = 1;
+ addend = rel->r_addend;
+ }
+ if (!update_plt_info (abfd, ifunc,
+ addend < 32768 ? NULL : got2, addend))
+ return FALSE;
+
+ if (htab->glink == NULL)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (!ppc_elf_create_glink (htab->elf.dynobj, info))
+ return FALSE;
+ }
+ }
+ }
switch (r_type)
{
@@ -3571,7 +3687,7 @@ ppc_elf_check_relocs (bfd *abfd,
break;
case R_PPC_PLTREL24:
- if (h == NULL)
+ if (h == NULL || ifunc != NULL)
break;
/* Fall through */
case R_PPC_PLT32:
@@ -3654,6 +3770,7 @@ ppc_elf_check_relocs (bfd *abfd,
case R_PPC_GLOB_DAT:
case R_PPC_JMP_SLOT:
case R_PPC_RELATIVE:
+ case R_PPC_IRELATIVE:
break;
/* These aren't handled yet. We'll report an error later. */
@@ -3782,7 +3899,9 @@ ppc_elf_check_relocs (bfd *abfd,
{
/* We may need a plt entry if the symbol turns out to be
a function defined in a dynamic object. */
- if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
+ h->needs_plt = 1;
+ if (ifunc == NULL
+ && !update_plt_info (abfd, &h->plt.plist, NULL, 0))
return FALSE;
break;
}
@@ -4291,6 +4410,33 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
}
r_type = ELF32_R_TYPE (rel->r_info);
+ if (!htab->is_vxworks && 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_refcounts != NULL)
+ {
+ struct plt_entry **local_plt = (struct plt_entry **)
+ (local_got_refcounts + symtab_hdr->sh_info);
+ char *local_got_tls_masks = (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)
+ {
+ bfd_vma addend = r_type == R_PPC_PLTREL24 ? rel->r_addend : 0;
+ struct plt_entry *ent = find_plt_ent (ifunc, got2, addend);
+ if (ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
+ continue;
+ }
+ }
+
switch (r_type)
{
case R_PPC_GOT_TLSLD16:
@@ -4405,17 +4551,7 @@ branch_reloc_hash_match (const bfd *ibfd,
enum elf_ppc_reloc_type r_type = ELF32_R_TYPE (rel->r_info);
unsigned int r_symndx = ELF32_R_SYM (rel->r_info);
- if (r_symndx >= symtab_hdr->sh_info
- && (r_type == R_PPC_PLTREL24
- || r_type == R_PPC_LOCAL24PC
- || r_type == R_PPC_REL14
- || r_type == R_PPC_REL14_BRTAKEN
- || r_type == R_PPC_REL14_BRNTAKEN
- || r_type == R_PPC_REL24
- || r_type == R_PPC_ADDR24
- || r_type == R_PPC_ADDR14
- || r_type == R_PPC_ADDR14_BRTAKEN
- || r_type == R_PPC_ADDR14_BRNTAKEN))
+ if (r_symndx >= symtab_hdr->sh_info && is_branch_reloc (r_type))
{
struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd);
struct elf_link_hash_entry *h;
@@ -4580,6 +4716,7 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
{
Elf_Internal_Sym *sym;
bfd_signed_vma *lgot_refs;
+ struct plt_entry **local_plt;
char *lgot_masks;
if (locsyms == NULL)
@@ -4600,7 +4737,9 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
lgot_refs = elf_local_got_refcounts (ibfd);
if (lgot_refs == NULL)
abort ();
- lgot_masks = (char *) (lgot_refs + symtab_hdr->sh_info);
+ local_plt = (struct plt_entry **)
+ (lgot_refs + symtab_hdr->sh_info);
+ lgot_masks = (char *) (local_plt + symtab_hdr->sh_info);
tls_mask = &lgot_masks[r_symndx];
got_count = &lgot_refs[r_symndx];
}
@@ -4690,6 +4829,7 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
/* Deal with function syms. */
if (h->type == STT_FUNC
+ || h->type == STT_GNU_IFUNC
|| h->needs_plt)
{
/* Clear procedure linkage table information for any symbol that
@@ -4699,9 +4839,10 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
if (ent->plt.refcount > 0)
break;
if (ent == NULL
- || SYMBOL_CALLS_LOCAL (info, h)
- || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
- && h->root.type == bfd_link_hash_undefweak))
+ || (h->type != STT_GNU_IFUNC
+ && (SYMBOL_CALLS_LOCAL (info, h)
+ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ && h->root.type == bfd_link_hash_undefweak))))
{
/* A PLT entry is not required/allowed when:
@@ -4937,29 +5078,37 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
htab = ppc_elf_hash_table (info);
- if (htab->elf.dynamic_sections_created)
+ if (htab->elf.dynamic_sections_created
+ || h->type == STT_GNU_IFUNC)
{
struct plt_entry *ent;
bfd_boolean doneone = FALSE;
bfd_vma plt_offset = 0, glink_offset = 0;
+ bfd_boolean dyn;
for (ent = h->plt.plist; ent != NULL; ent = ent->next)
if (ent->plt.refcount > 0)
{
/* Make sure this symbol is output as a dynamic symbol. */
if (h->dynindx == -1
- && !h->forced_local)
+ && !h->forced_local
+ && !h->def_regular
+ && htab->elf.dynamic_sections_created)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
+ dyn = htab->elf.dynamic_sections_created;
if (info->shared
- || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
+ || h->type == STT_GNU_IFUNC
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))
{
asection *s = htab->plt;
+ if (!dyn)
+ s = htab->iplt;
- if (htab->plt_type == PLT_NEW)
+ if (htab->plt_type == PLT_NEW || !dyn)
{
if (!doneone)
{
@@ -5037,29 +5186,35 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
/* We also need to make an entry in the .rela.plt section. */
if (!doneone)
{
- htab->relplt->size += sizeof (Elf32_External_Rela);
-
- if (htab->plt_type == PLT_VXWORKS)
+ if (!htab->elf.dynamic_sections_created)
+ htab->reliplt->size += sizeof (Elf32_External_Rela);
+ else
{
- /* Allocate space for the unloaded relocations. */
- if (!info->shared)
+ htab->relplt->size += sizeof (Elf32_External_Rela);
+
+ if (htab->plt_type == PLT_VXWORKS)
{
- if (ent->plt.offset
- == (bfd_vma) htab->plt_initial_entry_size)
+ /* Allocate space for the unloaded relocations. */
+ if (!info->shared
+ && htab->elf.dynamic_sections_created)
{
+ if (ent->plt.offset
+ == (bfd_vma) htab->plt_initial_entry_size)
+ {
+ htab->srelplt2->size
+ += (sizeof (Elf32_External_Rela)
+ * VXWORKS_PLTRESOLVE_RELOCS);
+ }
+
htab->srelplt2->size
- += sizeof (Elf32_External_Rela)
- * VXWORKS_PLTRESOLVE_RELOCS;
+ += (sizeof (Elf32_External_Rela)
+ * VXWORKS_PLT_NON_JMP_SLOT_RELOCS);
}
- htab->srelplt2->size
- += sizeof (Elf32_External_Rela)
- * VXWORKS_PLT_NON_JMP_SLOT_RELOCS;
+ /* Every PLT entry has an associated GOT entry in
+ .got.plt. */
+ htab->sgotplt->size += 4;
}
-
- /* Every PLT entry has an associated GOT entry in
- .got.plt. */
- htab->sgotplt->size += 4;
}
doneone = TRUE;
}
@@ -5091,6 +5246,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
/* Make sure this symbol is output as a dynamic symbol. */
if (eh->elf.dynindx == -1
&& !eh->elf.forced_local
+ && !eh->elf.def_regular
&& htab->elf.dynamic_sections_created)
{
if (!bfd_elf_link_record_dynamic_symbol (info, &eh->elf))
@@ -5206,7 +5362,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
/* Make sure undefined weak symbols are output as a dynamic
symbol in PIEs. */
else if (h->dynindx == -1
- && !h->forced_local)
+ && !h->forced_local
+ && !h->def_regular)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
@@ -5225,7 +5382,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
/* 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)
+ && !h->forced_local
+ && !h->def_regular)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
@@ -5315,6 +5473,8 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
{
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
+ struct plt_entry **local_plt;
+ struct plt_entry **end_local_plt;
char *lgot_masks;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
@@ -5365,7 +5525,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
symtab_hdr = &elf_symtab_hdr (ibfd);
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
- lgot_masks = (char *) end_local_got;
+ local_plt = (struct plt_entry **) end_local_got;
+ end_local_plt = local_plt + locsymcount;
+ lgot_masks = (char *) end_local_plt;
for (; local_got < end_local_got; ++local_got, ++lgot_masks)
if (*local_got > 0)
{
@@ -5395,6 +5557,46 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
}
else
*local_got = (bfd_vma) -1;
+
+ if (htab->is_vxworks)
+ continue;
+
+ /* Allocate space for calls to local STT_GNU_IFUNC syms in .iplt. */
+ for (; local_plt < end_local_plt; ++local_plt)
+ {
+ struct plt_entry *ent;
+ bfd_boolean doneone = FALSE;
+ bfd_vma plt_offset = 0, glink_offset = 0;
+
+ for (ent = *local_plt; ent != NULL; ent = ent->next)
+ if (ent->plt.refcount > 0)
+ {
+ asection *s = htab->iplt;
+
+ if (!doneone)
+ {
+ plt_offset = s->size;
+ s->size += 4;
+ }
+ ent->plt.offset = plt_offset;
+
+ s = htab->glink;
+ if (!doneone || info->shared || info->pie)
+ {
+ glink_offset = s->size;
+ s->size += GLINK_ENTRY_SIZE;
+ }
+ ent->glink_offset = glink_offset;
+
+ if (!doneone)
+ {
+ htab->reliplt += sizeof (Elf32_External_Rela);
+ doneone = TRUE;
+ }
+ }
+ else
+ ent->plt.offset = (bfd_vma) -1;
+ }
}
/* Allocate space for global sym dynamic relocs. */
@@ -5428,7 +5630,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
htab->elf.hgot->root.u.def.value = g_o_t;
}
- if (htab->glink != NULL && htab->glink->size != 0)
+ if (htab->glink != NULL
+ && htab->glink->size != 0
+ && htab->elf.dynamic_sections_created)
{
htab->glink_pltresolve = htab->glink->size;
/* Space for the branch table. */
@@ -5484,22 +5688,23 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
continue;
if (s == htab->plt
- || s == htab->glink
- || s == htab->got
- || s == htab->sgotplt
- || s == htab->sbss
- || s == htab->dynbss
- || s == htab->dynsbss)
+ || s == htab->got)
{
/* We'd like to strip these sections if they aren't needed, but if
we've exported dynamic symbols from them we must leave them.
It's too late to tell BFD to get rid of the symbols. */
- if ((s == htab->plt || s == htab->got) && htab->elf.hplt != NULL)
+ if (htab->elf.hplt != NULL)
strip_section = FALSE;
/* Strip this section if we don't need it; see the
comment below. */
}
- else if (s == htab->sdata[0].section
+ else if (s == htab->iplt
+ || s == htab->glink
+ || s == htab->sgotplt
+ || s == htab->sbss
+ || s == htab->dynbss
+ || s == htab->dynsbss
+ || s == htab->sdata[0].section
|| s == htab->sdata[1].section)
{
/* Strip these too. */
@@ -6192,6 +6397,73 @@ elf_finish_pointer_linker_section (bfd *input_bfd,
return relocation - linker_section_ptr->addend;
}
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
+static void
+write_glink_stub (struct plt_entry *ent, asection *plt_sec,
+ struct bfd_link_info *info)
+{
+ struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+ bfd *output_bfd = info->output_bfd;
+ bfd_vma plt;
+ unsigned char *p;
+
+ plt = ((ent->plt.offset & ~1)
+ + plt_sec->output_section->vma
+ + plt_sec->output_offset);
+ p = (unsigned char *) htab->glink->contents + ent->glink_offset;
+
+ if (info->shared || info->pie)
+ {
+ bfd_vma got = 0;
+
+ if (ent->addend >= 32768)
+ got = (ent->addend
+ + ent->sec->output_section->vma
+ + ent->sec->output_offset);
+ else if (htab->elf.hgot != NULL)
+ got = SYM_VAL (htab->elf.hgot);
+
+ plt -= got;
+
+ if (plt + 0x8000 < 0x10000)
+ {
+ bfd_put_32 (output_bfd, LWZ_11_30 + PPC_LO (plt), p);
+ p += 4;
+ bfd_put_32 (output_bfd, MTCTR_11, p);
+ p += 4;
+ bfd_put_32 (output_bfd, BCTR, p);
+ p += 4;
+ bfd_put_32 (output_bfd, NOP, p);
+ p += 4;
+ }
+ else
+ {
+ bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (plt), p);
+ p += 4;
+ bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
+ p += 4;
+ bfd_put_32 (output_bfd, MTCTR_11, p);
+ p += 4;
+ bfd_put_32 (output_bfd, BCTR, p);
+ p += 4;
+ }
+ }
+ else
+ {
+ bfd_put_32 (output_bfd, LIS_11 + PPC_HA (plt), p);
+ p += 4;
+ bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
+ p += 4;
+ bfd_put_32 (output_bfd, MTCTR_11, p);
+ p += 4;
+ bfd_put_32 (output_bfd, BCTR, p);
+ p += 4;
+ }
+}
+
/* The RELOCATE_SECTION function is called by the ELF backend linker
to handle the relocations for a section.
@@ -6285,6 +6557,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
bfd_boolean unresolved_reloc;
bfd_boolean warned;
unsigned int tls_type, tls_mask, tls_gd;
+ struct plt_entry **ifunc;
r_type = ELF32_R_TYPE (rel->r_info);
sym = NULL;
@@ -6349,8 +6622,11 @@ ppc_elf_relocate_section (bfd *output_bfd,
tls_mask = ((struct ppc_elf_link_hash_entry *) h)->tls_mask;
else if (local_got_offsets != NULL)
{
+ struct plt_entry **local_plt;
char *lgot_masks;
- lgot_masks = (char *) (local_got_offsets + symtab_hdr->sh_info);
+ local_plt
+ = (struct plt_entry **) (local_got_offsets + symtab_hdr->sh_info);
+ lgot_masks = (char *) (local_plt + symtab_hdr->sh_info);
tls_mask = lgot_masks[r_symndx];
}
@@ -6371,7 +6647,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
case R_PPC_GOT_TPREL16:
case R_PPC_GOT_TPREL16_LO:
- if (tls_mask != 0
+ if ((tls_mask & TLS_TLS) != 0
&& (tls_mask & TLS_TPREL) == 0)
{
bfd_vma insn;
@@ -6385,7 +6661,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC_TLS:
- if (tls_mask != 0
+ if ((tls_mask & TLS_TLS) != 0
&& (tls_mask & TLS_TPREL) == 0)
{
bfd_vma insn, rtra;
@@ -6432,13 +6708,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
case R_PPC_GOT_TLSGD16_HI:
case R_PPC_GOT_TLSGD16_HA:
tls_gd = TLS_TPRELGD;
- if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
goto tls_gdld_hi;
break;
case R_PPC_GOT_TLSLD16_HI:
case R_PPC_GOT_TLSLD16_HA:
- if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
{
tls_gdld_hi:
if ((tls_mask & tls_gd) != 0)
@@ -6457,13 +6733,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
case R_PPC_GOT_TLSGD16:
case R_PPC_GOT_TLSGD16_LO:
tls_gd = TLS_TPRELGD;
- if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
goto tls_ldgd_opt;
break;
case R_PPC_GOT_TLSLD16:
case R_PPC_GOT_TLSLD16_LO:
- if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
{
unsigned int insn1, insn2;
bfd_vma offset;
@@ -6543,7 +6819,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC_TLSGD:
- if (tls_mask != 0 && (tls_mask & TLS_GD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0)
{
unsigned int insn2;
bfd_vma offset = rel->r_offset;
@@ -6571,7 +6847,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC_TLSLD:
- if (tls_mask != 0 && (tls_mask & TLS_LD) == 0)
+ if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0)
{
unsigned int insn2;
@@ -6635,6 +6911,64 @@ ppc_elf_relocate_section (bfd *output_bfd,
break;
}
+ ifunc = NULL;
+ if (!htab->is_vxworks && is_branch_reloc (r_type))
+ {
+ if (h != NULL)
+ {
+ if (h->type == STT_GNU_IFUNC)
+ ifunc = &h->plt.plist;
+ }
+ else if (local_got_offsets != NULL)
+ {
+ if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ {
+ struct plt_entry **local_plt = (struct plt_entry **)
+ (local_got_offsets + symtab_hdr->sh_info);
+
+ ifunc = local_plt + r_symndx;
+ }
+ }
+ if (ifunc != NULL)
+ {
+ struct plt_entry *ent = find_plt_ent (ifunc, got2, rel->r_addend);
+
+ if (h == NULL && (ent->plt.offset & 1) == 0)
+ {
+ Elf_Internal_Rela rela;
+ bfd_byte *loc;
+
+ rela.r_offset = (htab->iplt->output_section->vma
+ + htab->iplt->output_offset
+ + ent->plt.offset);
+ rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
+ rela.r_addend = relocation;
+ loc = (htab->reliplt->contents
+ + ent->plt.offset * sizeof (Elf32_External_Rela) / 4);
+ bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+
+ ent->plt.offset |= 1;
+ }
+ if (h == NULL && (ent->glink_offset & 1) == 0)
+ {
+ write_glink_stub (ent, htab->iplt, info);
+ ent->glink_offset |= 1;
+ }
+
+ unresolved_reloc = FALSE;
+ if (htab->plt_type == PLT_NEW
+ || !htab->elf.dynamic_sections_created
+ || h == NULL)
+ relocation = (htab->glink->output_section->vma
+ + htab->glink->output_offset
+ + (ent->glink_offset & ~1));
+ else
+ relocation = (htab->plt->output_section->vma
+ + htab->plt->output_offset
+ + ent->plt.offset);
+ }
+ }
+
addend = rel->r_addend;
tls_type = 0;
howto = NULL;
@@ -7217,7 +7551,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
break;
case R_PPC_PLTREL24:
- if (h == NULL)
+ if (h == NULL || ifunc != NULL)
break;
/* Relocation is to the entry for this symbol in the
procedure linkage table. */
@@ -7389,6 +7723,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
case R_PPC_GLOB_DAT:
case R_PPC_JMP_SLOT:
case R_PPC_RELATIVE:
+ case R_PPC_IRELATIVE:
case R_PPC_PLT32:
case R_PPC_PLTREL32:
case R_PPC_PLT16_LO:
@@ -7524,10 +7859,6 @@ ppc_elf_relocate_section (bfd *output_bfd,
return ret;
}
-#define PPC_LO(v) ((v) & 0xffff)
-#define PPC_HI(v) (((v) >> 16) & 0xffff)
-#define PPC_HA(v) PPC_HI ((v) + 0x8000)
-
/* Finish up dynamic symbol handling. We set the contents of various
dynamic sections here. */
@@ -7559,7 +7890,8 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
bfd_byte *loc;
bfd_vma reloc_index;
- if (htab->plt_type == PLT_NEW)
+ if (htab->plt_type == PLT_NEW
+ || !htab->elf.dynamic_sections_created)
reloc_index = ent->plt.offset / 4;
else
{
@@ -7572,7 +7904,8 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
/* This symbol has an entry in the procedure linkage table.
Set it up. */
- if (htab->plt_type == PLT_VXWORKS)
+ if (htab->plt_type == PLT_VXWORKS
+ && htab->elf.dynamic_sections_created)
{
bfd_vma got_offset;
const bfd_vma *plt_entry;
@@ -7694,10 +8027,15 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
}
else
{
- rela.r_offset = (htab->plt->output_section->vma
- + htab->plt->output_offset
+ asection *splt = htab->plt;
+ if (!htab->elf.dynamic_sections_created)
+ splt = htab->iplt;
+
+ rela.r_offset = (splt->output_section->vma
+ + splt->output_offset
+ ent->plt.offset);
- if (htab->plt_type == PLT_OLD)
+ if (htab->plt_type == PLT_OLD
+ || !htab->elf.dynamic_sections_created)
{
/* We don't need to fill in the .plt. The ppc dynamic
linker will fill it in. */
@@ -7708,16 +8046,30 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
+ htab->glink->output_section->vma
+ htab->glink->output_offset);
bfd_put_32 (output_bfd, val,
- htab->plt->contents + ent->plt.offset);
+ splt->contents + ent->plt.offset);
}
}
/* Fill in the entry in the .rela.plt section. */
- rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
rela.r_addend = 0;
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
+ {
+ BFD_ASSERT (h->type == STT_GNU_IFUNC
+ && h->def_regular
+ && (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak));
+ rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
+ rela.r_addend = SYM_VAL (h);
+ }
+ else
+ rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
- loc = (htab->relplt->contents
- + reloc_index * sizeof (Elf32_External_Rela));
+ if (!htab->elf.dynamic_sections_created)
+ loc = htab->reliplt->contents;
+ else
+ loc = htab->relplt->contents;
+ loc += reloc_index * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
if (!h->def_regular)
@@ -7743,66 +8095,18 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
doneone = TRUE;
}
- if (htab->plt_type == PLT_NEW)
+ if (htab->plt_type == PLT_NEW
+ || !htab->elf.dynamic_sections_created)
{
- bfd_vma plt;
- unsigned char *p;
+ asection *splt = htab->plt;
+ if (!htab->elf.dynamic_sections_created)
+ splt = htab->iplt;
- plt = (ent->plt.offset
- + htab->plt->output_section->vma
- + htab->plt->output_offset);
- p = (unsigned char *) htab->glink->contents + ent->glink_offset;
+ write_glink_stub (ent, splt, info);
- if (info->shared || info->pie)
- {
- bfd_vma got = 0;
-
- if (ent->addend >= 32768)
- got = (ent->addend
- + ent->sec->output_section->vma
- + ent->sec->output_offset);
- else if (htab->elf.hgot != NULL)
- got = SYM_VAL (htab->elf.hgot);
-
- plt -= got;
-
- if (plt + 0x8000 < 0x10000)
- {
- bfd_put_32 (output_bfd, LWZ_11_30 + PPC_LO (plt), p);
- p += 4;
- bfd_put_32 (output_bfd, MTCTR_11, p);
- p += 4;
- bfd_put_32 (output_bfd, BCTR, p);
- p += 4;
- bfd_put_32 (output_bfd, NOP, p);
- p += 4;
- }
- else
- {
- bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (plt), p);
- p += 4;
- bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
- p += 4;
- bfd_put_32 (output_bfd, MTCTR_11, p);
- p += 4;
- bfd_put_32 (output_bfd, BCTR, p);
- p += 4;
- }
- }
- else
- {
- bfd_put_32 (output_bfd, LIS_11 + PPC_HA (plt), p);
- p += 4;
- bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
- p += 4;
- bfd_put_32 (output_bfd, MTCTR_11, p);
- p += 4;
- bfd_put_32 (output_bfd, BCTR, p);
- p += 4;
-
- /* We only need one non-PIC glink stub. */
- break;
- }
+ if (!info->shared && !info->pie)
+ /* We only need one non-PIC glink stub. */
+ break;
}
else
break;
@@ -8073,7 +8377,9 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
}
}
- if (htab->glink != NULL && htab->glink->contents != NULL)
+ if (htab->glink != NULL
+ && htab->glink->contents != NULL
+ && htab->elf.dynamic_sections_created)
{
unsigned char *p;
unsigned char *endp;
@@ -8340,6 +8646,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
#define elf_backend_plt_sym_val ppc_elf_plt_sym_val
#define elf_backend_action_discarded ppc_elf_action_discarded
#define elf_backend_init_index_section _bfd_elf_init_1_index_section
+#define elf_backend_post_process_headers _bfd_elf_set_osabi
#include "elf32-target.h"
@@ -8449,5 +8756,6 @@ ppc_elf_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
#undef elf32_bed
#define elf32_bed ppc_elf_vxworks_bed
+#undef elf_backend_post_process_headers
#include "elf32-target.h"