aboutsummaryrefslogtreecommitdiff
path: root/bfd/elfxx-sparc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elfxx-sparc.c')
-rw-r--r--bfd/elfxx-sparc.c538
1 files changed, 479 insertions, 59 deletions
diff --git a/bfd/elfxx-sparc.c b/bfd/elfxx-sparc.c
index e0d125f..bc36920 100644
--- a/bfd/elfxx-sparc.c
+++ b/bfd/elfxx-sparc.c
@@ -31,6 +31,8 @@
#include "opcode/sparc.h"
#include "elfxx-sparc.h"
#include "elf-vxworks.h"
+#include "objalloc.h"
+#include "hashtab.h"
/* In case we're on a 32-bit machine, construct a 64-bit "-1" value. */
#define MINUS_ONE (~ (bfd_vma) 0)
@@ -265,6 +267,10 @@ static reloc_howto_type _bfd_sparc_elf_howto_table[] =
HOWTO(R_SPARC_GOTDATA_OP_LOX10,0,2,0,FALSE,0,complain_overflow_dont, sparc_elf_lox10_reloc, "R_SPARC_GOTDATA_OP_LOX10",FALSE,0,0x000003ff, FALSE),
HOWTO(R_SPARC_GOTDATA_OP,0,0, 0,FALSE,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_GOTDATA_OP",FALSE,0,0x00000000,TRUE),
};
+static reloc_howto_type sparc_jmp_irel_howto =
+ HOWTO(R_SPARC_JMP_IREL, 0,0,00,FALSE,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_JMP_IREL",FALSE,0,0x00000000,TRUE);
+static reloc_howto_type sparc_irelative_howto =
+ HOWTO(R_SPARC_IRELATIVE, 0,0,00,FALSE,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_IRELATIVE",FALSE,0,0x00000000,TRUE);
static reloc_howto_type sparc_vtinherit_howto =
HOWTO (R_SPARC_GNU_VTINHERIT, 0,2,0,FALSE,0,complain_overflow_dont, NULL, "R_SPARC_GNU_VTINHERIT", FALSE,0, 0, FALSE);
static reloc_howto_type sparc_vtentry_howto =
@@ -360,6 +366,8 @@ static const struct elf_reloc_map sparc_reloc_map[] =
{ BFD_RELOC_SPARC_GOTDATA_OP_LOX10, R_SPARC_GOTDATA_OP_LOX10 },
{ BFD_RELOC_SPARC_GOTDATA_OP, R_SPARC_GOTDATA_OP },
{ BFD_RELOC_SPARC_REGISTER, R_SPARC_REGISTER },
+ { BFD_RELOC_SPARC_JMP_IREL, R_SPARC_JMP_IREL },
+ { BFD_RELOC_SPARC_IRELATIVE, R_SPARC_IRELATIVE },
{ BFD_RELOC_VTABLE_INHERIT, R_SPARC_GNU_VTINHERIT },
{ BFD_RELOC_VTABLE_ENTRY, R_SPARC_GNU_VTENTRY },
{ BFD_RELOC_SPARC_REV32, R_SPARC_REV32 },
@@ -373,6 +381,12 @@ _bfd_sparc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
switch (code)
{
+ case BFD_RELOC_SPARC_JMP_IREL:
+ return &sparc_jmp_irel_howto;
+
+ case BFD_RELOC_SPARC_IRELATIVE:
+ return &sparc_irelative_howto;
+
case BFD_RELOC_VTABLE_INHERIT:
return &sparc_vtinherit_howto;
@@ -425,6 +439,12 @@ _bfd_sparc_elf_info_to_howto_ptr (unsigned int r_type)
{
switch (r_type)
{
+ case R_SPARC_JMP_IREL:
+ return &sparc_jmp_irel_howto;
+
+ case R_SPARC_IRELATIVE:
+ return &sparc_irelative_howto;
+
case R_SPARC_GNU_VTINHERIT:
return &sparc_vtinherit_howto;
@@ -831,6 +851,78 @@ link_hash_newfunc (struct bfd_hash_entry *entry,
#define ELF32_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
#define ELF64_DYNAMIC_INTERPRETER "/usr/lib/sparcv9/ld.so.1"
+/* Compute a hash of a local hash entry. We use elf_link_hash_entry
+ for local symbol so that we can handle local STT_GNU_IFUNC symbols
+ as global symbol. We reuse indx and dynstr_index for local symbol
+ hash since they aren't used by global symbols in this backend. */
+
+static hashval_t
+elf_sparc_local_htab_hash (const void *ptr)
+{
+ struct elf_link_hash_entry *h
+ = (struct elf_link_hash_entry *) ptr;
+ return ELF_LOCAL_SYMBOL_HASH (h->indx, h->dynstr_index);
+}
+
+/* Compare local hash entries. */
+
+static int
+elf_sparc_local_htab_eq (const void *ptr1, const void *ptr2)
+{
+ struct elf_link_hash_entry *h1
+ = (struct elf_link_hash_entry *) ptr1;
+ struct elf_link_hash_entry *h2
+ = (struct elf_link_hash_entry *) ptr2;
+
+ return h1->indx == h2->indx && h1->dynstr_index == h2->dynstr_index;
+}
+
+/* Find and/or create a hash entry for local symbol. */
+
+static struct elf_link_hash_entry *
+elf_sparc_get_local_sym_hash (struct _bfd_sparc_elf_link_hash_table *htab,
+ bfd *abfd, const Elf_Internal_Rela *rel,
+ bfd_boolean create)
+{
+ struct _bfd_sparc_elf_link_hash_entry e, *ret;
+ asection *sec = abfd->sections;
+ unsigned long r_symndx;
+ hashval_t h;
+ void **slot;
+
+ r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info);
+ h = ELF_LOCAL_SYMBOL_HASH (sec->id, r_symndx);
+
+ e.elf.indx = sec->id;
+ e.elf.dynstr_index = r_symndx;
+ slot = htab_find_slot_with_hash (htab->loc_hash_table, &e, h,
+ create ? INSERT : NO_INSERT);
+
+ if (!slot)
+ return NULL;
+
+ if (*slot)
+ {
+ ret = (struct _bfd_sparc_elf_link_hash_entry *) *slot;
+ return &ret->elf;
+ }
+
+ ret = (struct _bfd_sparc_elf_link_hash_entry *)
+ objalloc_alloc ((struct objalloc *) htab->loc_hash_memory,
+ sizeof (struct _bfd_sparc_elf_link_hash_entry));
+ if (ret)
+ {
+ memset (ret, 0, sizeof (*ret));
+ ret->elf.indx = sec->id;
+ ret->elf.dynstr_index = r_symndx;
+ ret->elf.dynindx = -1;
+ ret->elf.plt.offset = (bfd_vma) -1;
+ ret->elf.got.offset = (bfd_vma) -1;
+ *slot = ret;
+ }
+ return &ret->elf;
+}
+
/* Create a SPARC ELF linker hash table. */
struct bfd_link_hash_table *
@@ -857,6 +949,10 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
ret->bytes_per_rela = sizeof (Elf64_External_Rela);
ret->dynamic_interpreter = ELF64_DYNAMIC_INTERPRETER;
ret->dynamic_interpreter_size = sizeof ELF64_DYNAMIC_INTERPRETER;
+
+ ret->build_plt_entry = sparc64_plt_entry_build;
+ ret->plt_header_size = PLT64_HEADER_SIZE;
+ ret->plt_entry_size = PLT64_ENTRY_SIZE;
}
else
{
@@ -872,6 +968,10 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
ret->bytes_per_rela = sizeof (Elf32_External_Rela);
ret->dynamic_interpreter = ELF32_DYNAMIC_INTERPRETER;
ret->dynamic_interpreter_size = sizeof ELF32_DYNAMIC_INTERPRETER;
+
+ ret->build_plt_entry = sparc32_plt_entry_build;
+ ret->plt_header_size = PLT32_HEADER_SIZE;
+ ret->plt_entry_size = PLT32_ENTRY_SIZE;
}
if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc,
@@ -882,9 +982,35 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd)
return NULL;
}
+ ret->loc_hash_table = htab_try_create (1024,
+ elf_sparc_local_htab_hash,
+ elf_sparc_local_htab_eq,
+ NULL);
+ ret->loc_hash_memory = objalloc_create ();
+ if (!ret->loc_hash_table || !ret->loc_hash_memory)
+ {
+ free (ret);
+ return NULL;
+ }
+
return &ret->elf.root;
}
+/* Destroy a SPARC ELF linker hash table. */
+
+void
+_bfd_sparc_elf_link_hash_table_free (struct bfd_link_hash_table *hash)
+{
+ struct _bfd_sparc_elf_link_hash_table *htab
+ = (struct _bfd_sparc_elf_link_hash_table *) hash;
+
+ if (htab->loc_hash_table)
+ htab_delete (htab->loc_hash_table);
+ if (htab->loc_hash_memory)
+ objalloc_free ((struct objalloc *) htab->loc_hash_memory);
+ _bfd_generic_link_hash_table_free (hash);
+}
+
/* Create .plt, .rela.plt, .got, .rela.got, .dynbss, and
.rela.bss sections in DYNOBJ, and set up shortcuts to them in our
hash table. */
@@ -924,21 +1050,6 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj,
= 4 * ARRAY_SIZE (sparc_vxworks_exec_plt_entry);
}
}
- else
- {
- if (ABI_64_P (dynobj))
- {
- htab->build_plt_entry = sparc64_plt_entry_build;
- htab->plt_header_size = PLT64_HEADER_SIZE;
- htab->plt_entry_size = PLT64_ENTRY_SIZE;
- }
- else
- {
- htab->build_plt_entry = sparc32_plt_entry_build;
- htab->plt_header_size = PLT32_HEADER_SIZE;
- htab->plt_entry_size = PLT32_ENTRY_SIZE;
- }
- }
if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
|| (!info->shared && !htab->srelbss))
@@ -947,6 +1058,37 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj,
return TRUE;
}
+static bfd_boolean
+create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
+{
+ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+ struct elf_link_hash_table *htab = elf_hash_table (info);
+ flagword flags, pltflags;
+ asection *s;
+
+ if (htab->irelifunc != NULL || htab->iplt != NULL)
+ return TRUE;
+
+ flags = bed->dynamic_sec_flags;
+ pltflags = flags | SEC_ALLOC | SEC_CODE | SEC_LOAD;
+
+ s = bfd_make_section_with_flags (abfd, ".iplt", pltflags);
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
+ return FALSE;
+ htab->iplt = s;
+
+ s = bfd_make_section_with_flags (abfd, ".rela.iplt",
+ flags | SEC_READONLY);
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s,
+ bed->s->log_file_align))
+ return FALSE;
+ htab->irelplt = s;
+
+ return TRUE;
+}
+
/* Copy the extra info we tack onto an elf_link_hash_entry. */
void
@@ -1074,12 +1216,18 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
BFD_ASSERT (is_sparc_elf (abfd) || num_relocs == 0);
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (!create_ifunc_sections (htab->elf.dynobj, info))
+ return FALSE;
+
rel_end = relocs + num_relocs;
for (rel = relocs; rel < rel_end; rel++)
{
unsigned int r_type;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
+ Elf_Internal_Sym *isym;
r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info);
r_type = SPARC_ELF_R_TYPE (rel->r_info);
@@ -1091,8 +1239,33 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
return FALSE;
}
+ isym = NULL;
if (r_symndx < symtab_hdr->sh_info)
- h = NULL;
+ {
+ /* A local symbol. */
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ /* Check relocation against local STT_GNU_IFUNC symbol. */
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ {
+ h = elf_sparc_get_local_sym_hash (htab, abfd, rel,
+ TRUE);
+ if (h == NULL)
+ return FALSE;
+
+ /* Fake a STT_GNU_IFUNC symbol. */
+ h->type = STT_GNU_IFUNC;
+ h->def_regular = 1;
+ h->ref_regular = 1;
+ h->forced_local = 1;
+ h->root.type = bfd_link_hash_defined;
+ }
+ else
+ h = NULL;
+ }
else
{
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
@@ -1101,6 +1274,12 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
+ if (h && h->type == STT_GNU_IFUNC)
+ {
+ if (h->def_regular)
+ h->plt.refcount += 1;
+ }
+
/* Compatibility with old R_SPARC_REV32 reloc conflicting
with R_SPARC_TLS_GD_HI22. */
if (! ABI_64_P (abfd) && ! checked_tlsgd)
@@ -1239,8 +1418,6 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (htab->elf.sgot == NULL)
{
- if (htab->elf.dynobj == NULL)
- htab->elf.dynobj = abfd;
if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
return FALSE;
}
@@ -1403,7 +1580,10 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
&& (sec->flags & SEC_ALLOC) != 0
&& h != NULL
&& (h->root.type == bfd_link_hash_defweak
- || !h->def_regular)))
+ || !h->def_regular))
+ || (!info->shared
+ && h != NULL
+ && h->type == STT_GNU_IFUNC))
{
struct _bfd_sparc_elf_dyn_relocs *p;
struct _bfd_sparc_elf_dyn_relocs **head;
@@ -1413,9 +1593,6 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
section in dynobj and make room for the reloc. */
if (sreloc == NULL)
{
- if (htab->elf.dynobj == NULL)
- htab->elf.dynobj = abfd;
-
sreloc = _bfd_elf_make_dynamic_reloc_section
(sec, htab->elf.dynobj, htab->word_align_power,
abfd, /*rela?*/ TRUE);
@@ -1435,13 +1612,8 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
easily. Oh well. */
asection *s;
void *vpp;
- Elf_Internal_Sym *isym;
-
- isym = bfd_sym_from_r_symndx (&htab->sym_cache,
- abfd, r_symndx);
- if (isym == NULL)
- return FALSE;
+ BFD_ASSERT (isym != NULL);
s = bfd_section_from_elf_index (abfd, isym->st_shndx);
if (s == NULL)
s = sec;
@@ -1684,6 +1856,7 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
/* Make sure we know what is going on here. */
BFD_ASSERT (htab->elf.dynobj != NULL
&& (h->needs_plt
+ || h->type == STT_GNU_IFUNC
|| h->u.weakdef != NULL
|| (h->def_dynamic
&& h->ref_regular
@@ -1697,6 +1870,7 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
some of their functions as STT_NOTYPE when they really should be
STT_FUNC. */
if (h->type == STT_FUNC
+ || h->type == STT_GNU_IFUNC
|| h->needs_plt
|| (h->type == STT_NOTYPE
&& (h->root.type == bfd_link_hash_defined
@@ -1704,9 +1878,10 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
&& (h->root.u.def.section->flags & SEC_CODE) != 0))
{
if (h->plt.refcount <= 0
- || 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))))
{
/* This case can occur if we saw a WPLT30 reloc in an input
file, but the symbol was never referred to by a dynamic
@@ -1828,8 +2003,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
htab = _bfd_sparc_elf_hash_table (info);
BFD_ASSERT (htab != NULL);
- if (htab->elf.dynamic_sections_created
- && h->plt.refcount > 0)
+ if ((htab->elf.dynamic_sections_created
+ && h->plt.refcount > 0)
+ || (h->type == STT_GNU_IFUNC
+ && h->def_regular))
{
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
@@ -1840,10 +2017,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
return FALSE;
}
- if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h))
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)
+ || h->type == STT_GNU_IFUNC)
{
asection *s = htab->elf.splt;
+ if (s == NULL)
+ s = htab->elf.iplt;
+
/* Allocate room for the header. */
if (s->size == 0)
{
@@ -1892,7 +2073,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
s->size += htab->plt_entry_size;
/* We also need to make an entry in the .rela.plt section. */
- htab->elf.srelplt->size += SPARC_ELF_RELA_BYTES (htab);
+ if (s == htab->elf.splt)
+ htab->elf.srelplt->size += SPARC_ELF_RELA_BYTES (htab);
+ else
+ htab->elf.irelplt->size += SPARC_ELF_RELA_BYTES (htab);
if (htab->is_vxworks)
{
@@ -1949,7 +2133,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
R_SPARC_TLS_GD_{HI22,LO10} needs one if local symbol and two if
global. */
if ((tls_type == GOT_TLS_GD && h->dynindx == -1)
- || tls_type == GOT_TLS_IE)
+ || tls_type == GOT_TLS_IE
+ || h->type == STT_GNU_IFUNC)
htab->elf.srelgot->size += SPARC_ELF_RELA_BYTES (htab);
else if (tls_type == GOT_TLS_GD)
htab->elf.srelgot->size += 2 * SPARC_ELF_RELA_BYTES (htab);
@@ -2060,6 +2245,25 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
return TRUE;
}
+/* Allocate space in .plt, .got and associated reloc sections for
+ local dynamic relocs. */
+
+static bfd_boolean
+allocate_local_dynrelocs (void **slot, void *inf)
+{
+ struct elf_link_hash_entry *h
+ = (struct elf_link_hash_entry *) *slot;
+
+ if (h->type != STT_GNU_IFUNC
+ || !h->def_regular
+ || !h->ref_regular
+ || !h->forced_local
+ || h->root.type != bfd_link_hash_defined)
+ abort ();
+
+ return allocate_dynrelocs (h, inf);
+}
+
/* Find any dynamic relocs that apply to read-only sections. */
static bfd_boolean
@@ -2172,6 +2376,8 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
else if (p->count != 0)
{
srel = elf_section_data (p->sec)->sreloc;
+ if (!htab->elf.dynamic_sections_created)
+ srel = htab->elf.irelplt;
srel->size += p->count * SPARC_ELF_RELA_BYTES (htab);
if ((p->sec->output_section->flags & SEC_READONLY) != 0)
info->flags |= DF_TEXTREL;
@@ -2222,6 +2428,9 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
sym dynamic relocs. */
elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info);
+ /* Allocate .plt and .got entries, and space for local symbols. */
+ htab_traverse (htab->loc_hash_table, allocate_local_dynrelocs, info);
+
if (! ABI_64_P (output_bfd)
&& !htab->is_vxworks
&& elf_hash_table (info)->dynamic_sections_created)
@@ -2251,6 +2460,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd,
if (s == htab->elf.splt
|| s == htab->elf.sgot
|| s == htab->sdynbss
+ || s == htab->elf.iplt
|| s == htab->elf.sgotplt)
{
/* Strip this section if we don't need it; see the
@@ -2544,6 +2754,20 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+
+ if (!info->relocatable
+ && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ {
+ /* Relocate against local STT_GNU_IFUNC symbol. */
+ h = elf_sparc_get_local_sym_hash (htab, input_bfd,
+ rel, FALSE);
+ if (h == NULL)
+ abort ();
+
+ /* Set STT_GNU_IFUNC symbol value. */
+ h->root.u.def.value = sym->st_value;
+ h->root.u.def.section = sec;
+ }
}
else
{
@@ -2580,6 +2804,111 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
if (info->relocatable)
continue;
+ if (h != NULL
+ && h->type == STT_GNU_IFUNC
+ && h->def_regular)
+ {
+ asection *plt_sec;
+ const char *name;
+
+ if ((input_section->flags & SEC_ALLOC) == 0
+ || h->plt.offset == (bfd_vma) -1)
+ abort ();
+
+ plt_sec = htab->elf.splt;
+ if (! plt_sec)
+ plt_sec =htab->elf.iplt;
+
+ switch (r_type)
+ {
+ case R_SPARC_GOTDATA_HIX22:
+ case R_SPARC_GOTDATA_LOX10:
+ case R_SPARC_GOTDATA_OP_HIX22:
+ case R_SPARC_GOTDATA_OP_LOX10:
+ case R_SPARC_GOT10:
+ case R_SPARC_GOT13:
+ case R_SPARC_GOT22:
+ if (htab->elf.sgot == NULL)
+ abort ();
+ off = h->got.offset;
+ if (off == (bfd_vma) -1)
+ abort();
+ relocation = htab->elf.sgot->output_offset + off - got_base;
+ goto do_relocation;
+
+ case R_SPARC_WPLT30:
+ case R_SPARC_WDISP30:
+ relocation = (plt_sec->output_section->vma
+ + plt_sec->output_offset + h->plt.offset);
+ goto do_relocation;
+
+ case R_SPARC_32:
+ case R_SPARC_64:
+ if (info->shared && h->non_got_ref)
+ {
+ Elf_Internal_Rela outrel;
+ bfd_vma offset;
+
+ offset = _bfd_elf_section_offset (output_bfd, info,
+ input_section,
+ rel->r_offset);
+ if (offset == (bfd_vma) -1
+ || offset == (bfd_vma) -2)
+ abort();
+
+ outrel.r_offset = (input_section->output_section->vma
+ + input_section->output_offset
+ + offset);
+
+ if (h->dynindx == -1
+ || h->forced_local
+ || info->executable)
+ {
+ outrel.r_info = SPARC_ELF_R_INFO (htab, NULL,
+ 0, R_SPARC_IRELATIVE);
+ outrel.r_addend = relocation + rel->r_addend;
+ }
+ else
+ {
+ if (h->dynindx == -1)
+ abort();
+ outrel.r_info = SPARC_ELF_R_INFO (htab, rel, h->dynindx, r_type);
+ outrel.r_addend = rel->r_addend;
+ }
+
+ sparc_elf_append_rela (output_bfd, sreloc, &outrel);
+ continue;
+ }
+
+ relocation = (plt_sec->output_section->vma
+ + plt_sec->output_offset + h->plt.offset);
+ goto do_relocation;
+
+ case R_SPARC_HI22:
+ case R_SPARC_LO10:
+ /* We should only see such relocs in static links. */
+ if (info->shared)
+ abort();
+ relocation = (plt_sec->output_section->vma
+ + plt_sec->output_offset + h->plt.offset);
+ goto do_relocation;
+
+ default:
+ if (h->root.root.string)
+ name = h->root.root.string;
+ else
+ name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym,
+ NULL);
+ (*_bfd_error_handler)
+ (_("%B: relocation %s against STT_GNU_IFUNC "
+ "symbol `%s' isn't handled by %s"), input_bfd,
+ _bfd_sparc_elf_howto_table[r_type].name,
+ name, __FUNCTION__);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ }
+
switch (r_type)
{
case R_SPARC_GOTDATA_HIX22:
@@ -3496,10 +3825,12 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd,
}
if (r == bfd_reloc_continue)
- r = _bfd_final_link_relocate (howto, input_bfd, input_section,
- contents, rel->r_offset,
- relocation, rel->r_addend);
-
+ {
+do_relocation:
+ r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+ contents, rel->r_offset,
+ relocation, rel->r_addend);
+ }
if (r != bfd_reloc_ok)
{
switch (r)
@@ -3681,13 +4012,21 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
bfd_vma r_offset, got_offset;
int rela_index;
- /* This symbol has an entry in the PLT. Set it up. */
-
- BFD_ASSERT (h->dynindx != -1);
+ /* When building a static executable, use .iplt and
+ .rela.iplt sections for STT_GNU_IFUNC symbols. */
+ if (htab->elf.splt != NULL)
+ {
+ splt = htab->elf.splt;
+ srela = htab->elf.srelplt;
+ }
+ else
+ {
+ splt = htab->elf.iplt;
+ srela = htab->elf.irelplt;
+ }
- splt = htab->elf.splt;
- srela = htab->elf.srelplt;
- BFD_ASSERT (splt != NULL && srela != NULL);
+ if (splt == NULL || srela == NULL)
+ abort ();
/* Fill in the entry in the .rela.plt section. */
if (htab->is_vxworks)
@@ -3710,29 +4049,73 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
+ htab->elf.sgotplt->output_offset
+ got_offset);
rela.r_addend = 0;
+ rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx,
+ R_SPARC_JMP_SLOT);
}
else
{
+ bfd_boolean ifunc = FALSE;
+
/* Fill in the entry in the procedure linkage table. */
rela_index = SPARC_ELF_BUILD_PLT_ENTRY (htab, output_bfd, splt,
h->plt.offset, splt->size,
&r_offset);
+ if (h == NULL
+ || h->dynindx == -1
+ || ((info->executable
+ || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ && h->def_regular
+ && h->type == STT_GNU_IFUNC))
+ {
+ ifunc = TRUE;
+ BFD_ASSERT (h == NULL
+ || (h->type == STT_GNU_IFUNC
+ && h->def_regular
+ && (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)));
+ }
+
rela.r_offset = r_offset
+ (splt->output_section->vma + splt->output_offset);
- if (! ABI_64_P (output_bfd)
- || h->plt.offset < (PLT64_LARGE_THRESHOLD * PLT64_ENTRY_SIZE))
+ if (ABI_64_P (output_bfd)
+ && h->plt.offset >= (PLT64_LARGE_THRESHOLD * PLT64_ENTRY_SIZE))
{
- rela.r_addend = 0;
+ if (ifunc)
+ {
+ rela.r_addend = (h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset
+ + h->root.u.def.value);
+ rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0,
+ R_SPARC_IRELATIVE);
+ }
+ else
+ {
+ rela.r_addend = (-(h->plt.offset + 4)
+ - splt->output_section->vma
+ - splt->output_offset);
+ rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx,
+ R_SPARC_JMP_SLOT);
+ }
}
else
{
- rela.r_addend = (-(h->plt.offset + 4)
- - splt->output_section->vma
- - splt->output_offset);
+ if (ifunc)
+ {
+ rela.r_addend = (h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset
+ + h->root.u.def.value);
+ rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0,
+ R_SPARC_JMP_IREL);
+ }
+ else
+ {
+ rela.r_addend = 0;
+ rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx,
+ R_SPARC_JMP_SLOT);
+ }
}
}
- rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx, R_SPARC_JMP_SLOT);
/* Adjust for the first 4 reserved elements in the .plt section
when setting the offset in the .rela.plt section.
@@ -3780,11 +4163,29 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
the symbol was forced to be local because of a version file.
The entry in the global offset table will already have been
initialized in the relocate_section function. */
- if (info->shared
- && SYMBOL_REFERENCES_LOCAL (info, h))
+ if (! info->shared
+ && h->type == STT_GNU_IFUNC
+ && h->def_regular)
+ {
+ asection *plt;
+
+ /* We load the GOT entry with the PLT entry. */
+ plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt;
+ SPARC_ELF_PUT_WORD (htab, output_bfd,
+ (plt->output_section->vma
+ + plt->output_offset + h->plt.offset),
+ htab->elf.sgot->contents
+ + (h->got.offset & ~(bfd_vma) 1));
+ return TRUE;
+ }
+ else if (info->shared
+ && SYMBOL_REFERENCES_LOCAL (info, h))
{
asection *sec = h->root.u.def.section;
- rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_RELATIVE);
+ if (h->type == STT_GNU_IFUNC)
+ rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_IRELATIVE);
+ else
+ rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_RELATIVE);
rela.r_addend = (h->root.u.def.value
+ sec->output_section->vma
+ sec->output_offset);
@@ -3823,9 +4224,10 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd,
/* Mark some specially defined symbols as absolute. On VxWorks,
_GLOBAL_OFFSET_TABLE_ is not absolute: it is relative to the
".got" section. Likewise _PROCEDURE_LINKAGE_TABLE_ and ".plt". */
- if (strcmp (h->root.root.string, "_DYNAMIC") == 0
- || (!htab->is_vxworks
- && (h == htab->elf.hgot || h == htab->elf.hplt)))
+ if (sym != NULL
+ && (strcmp (h->root.root.string, "_DYNAMIC") == 0
+ || (!htab->is_vxworks
+ && (h == htab->elf.hgot || h == htab->elf.hplt))))
sym->st_shndx = SHN_ABS;
return TRUE;
@@ -4021,6 +4423,21 @@ sparc_vxworks_finish_shared_plt (bfd *output_bfd, struct bfd_link_info *info)
htab->elf.splt->contents + i * 4);
}
+/* Finish up local dynamic symbol handling. We set the contents of
+ various dynamic sections here. */
+
+static bfd_boolean
+finish_local_dynamic_symbol (void **slot, void *inf)
+{
+ struct elf_link_hash_entry *h
+ = (struct elf_link_hash_entry *) *slot;
+ struct bfd_link_info *info
+ = (struct bfd_link_info *) inf;
+
+ return _bfd_sparc_elf_finish_dynamic_symbol (info->output_bfd, info,
+ h, NULL);
+}
+
bfd_boolean
_bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
{
@@ -4083,6 +4500,9 @@ _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *i
elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize =
SPARC_ELF_WORD_BYTES (htab);
+ /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols. */
+ htab_traverse (htab->loc_hash_table, finish_local_dynamic_symbol, info);
+
return TRUE;
}