/* IBM S/390-specific support for 64-bit ELF
- Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- 2010, 2011, 2012 Free Software Foundation, Inc.
+ Copyright (C) 2000-2015 Free Software Foundation, Inc.
Contributed Martin Schwidefsky (schwidefsky@de.ibm.com).
This file is part of BFD, the Binary File Descriptor library.
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
-
-static reloc_howto_type *elf_s390_reloc_type_lookup
- PARAMS ((bfd *, bfd_reloc_code_real_type));
-static void elf_s390_info_to_howto
- PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
-static bfd_boolean elf_s390_is_local_label_name
- PARAMS ((bfd *, const char *));
-static struct bfd_hash_entry *link_hash_newfunc
- PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
-static struct bfd_link_hash_table *elf_s390_link_hash_table_create
- PARAMS ((bfd *));
-static bfd_boolean create_got_section
- PARAMS((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_s390_create_dynamic_sections
- PARAMS((bfd *, struct bfd_link_info *));
-static void elf_s390_copy_indirect_symbol
- PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *,
- struct elf_link_hash_entry *));
-static bfd_boolean elf_s390_check_relocs
- PARAMS ((bfd *, struct bfd_link_info *, asection *,
- const Elf_Internal_Rela *));
-struct elf_s390_link_hash_entry;
-static void elf_s390_adjust_gotplt
- PARAMS ((struct elf_s390_link_hash_entry *));
-static bfd_boolean elf_s390_adjust_dynamic_symbol
- PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
-static bfd_boolean allocate_dynrelocs
- PARAMS ((struct elf_link_hash_entry *, PTR));
-static bfd_boolean readonly_dynrelocs
- PARAMS ((struct elf_link_hash_entry *, PTR));
-static bfd_boolean elf_s390_size_dynamic_sections
- PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_s390_relocate_section
- PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
- Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
-static bfd_boolean elf_s390_finish_dynamic_symbol
- PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
- Elf_Internal_Sym *));
-static enum elf_reloc_type_class elf_s390_reloc_type_class
- PARAMS ((const Elf_Internal_Rela *));
-static bfd_boolean elf_s390_finish_dynamic_sections
- PARAMS ((bfd *, struct bfd_link_info *));
-static bfd_boolean elf_s390_object_p
- PARAMS ((bfd *));
-static int elf_s390_tls_transition
- PARAMS ((struct bfd_link_info *, int, int));
-static bfd_reloc_status_type s390_tls_reloc
- PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
-static bfd_vma dtpoff_base
- PARAMS ((struct bfd_link_info *));
-static bfd_vma tpoff
- PARAMS ((struct bfd_link_info *, bfd_vma));
-static void invalid_tls_insn
- PARAMS ((bfd *, asection *, Elf_Internal_Rela *));
-static bfd_reloc_status_type s390_elf_ldisp_reloc
- PARAMS ((bfd *, arelent *, asymbol *, PTR, asection *, bfd *, char **));
-
#include "elf/s390.h"
/* In case we're on a 32-bit machine, construct a 64-bit "-1" value
from smaller values. Start with zero, widen, *then* decrement. */
#define MINUS_ONE (((bfd_vma)0) - 1)
+static bfd_reloc_status_type
+s390_tls_reloc (bfd *, arelent *, asymbol *, void *,
+ asection *, bfd *, char **);
+static bfd_reloc_status_type
+s390_elf_ldisp_reloc (bfd *, arelent *, asymbol *, void *,
+ asection *, bfd *, char **);
+
/* The relocation "howto" table. */
static reloc_howto_type elf_howto_table[] =
{
HOWTO (R_390_NONE, /* type */
0, /* rightshift */
- 0, /* size (0 = byte, 1 = short, 2 = long) */
+ 3, /* size (0 = byte, 1 = 2 byte, 2 = 4 byte) */
0, /* bitsize */
FALSE, /* pc_relative */
0, /* bitpos */
s390_elf_ldisp_reloc, "R_390_GOTPLT20", FALSE, 0,0x0fffff00, FALSE),
HOWTO(R_390_TLS_GOTIE20, 0, 2, 20, FALSE, 8, complain_overflow_dont,
s390_elf_ldisp_reloc, "R_390_TLS_GOTIE20", FALSE, 0,0x0fffff00, FALSE),
+ HOWTO(R_390_IRELATIVE, 0, 4, 64, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_IRELATIVE", FALSE, 0, MINUS_ONE, FALSE),
+ HOWTO(R_390_PC12DBL, 1, 1, 12, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PC12DBL", FALSE, 0,0x00000fff, TRUE),
+ HOWTO(R_390_PLT12DBL, 1, 1, 12, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLT12DBL", FALSE, 0,0x00000fff, TRUE),
+ HOWTO(R_390_PC24DBL, 1, 2, 24, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PC24DBL", FALSE, 0,0x00ffffff, TRUE),
+ HOWTO(R_390_PLT24DBL, 1, 2, 24, TRUE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_390_PLT24DBL", FALSE, 0,0x00ffffff, TRUE),
};
/* GNU extension to record C++ vtable hierarchy. */
HOWTO (R_390_GNU_VTENTRY, 0,4,0,FALSE,0,complain_overflow_dont, _bfd_elf_rel_vtable_reloc_fn,"R_390_GNU_VTENTRY", FALSE,0,0, FALSE);
static reloc_howto_type *
-elf_s390_reloc_type_lookup (abfd, code)
- bfd *abfd ATTRIBUTE_UNUSED;
- bfd_reloc_code_real_type code;
+elf_s390_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+ bfd_reloc_code_real_type code)
{
switch (code)
{
return &elf_howto_table[(int) R_390_GOT16];
case BFD_RELOC_16_PCREL:
return &elf_howto_table[(int) R_390_PC16];
+ case BFD_RELOC_390_PC12DBL:
+ return &elf_howto_table[(int) R_390_PC12DBL];
+ case BFD_RELOC_390_PLT12DBL:
+ return &elf_howto_table[(int) R_390_PLT12DBL];
case BFD_RELOC_390_PC16DBL:
return &elf_howto_table[(int) R_390_PC16DBL];
case BFD_RELOC_390_PLT16DBL:
return &elf_howto_table[(int) R_390_PLT16DBL];
+ case BFD_RELOC_390_PC24DBL:
+ return &elf_howto_table[(int) R_390_PC24DBL];
+ case BFD_RELOC_390_PLT24DBL:
+ return &elf_howto_table[(int) R_390_PLT24DBL];
case BFD_RELOC_390_PC32DBL:
return &elf_howto_table[(int) R_390_PC32DBL];
case BFD_RELOC_390_PLT32DBL:
return &elf_howto_table[(int) R_390_GOTPLT20];
case BFD_RELOC_390_TLS_GOTIE20:
return &elf_howto_table[(int) R_390_TLS_GOTIE20];
+ case BFD_RELOC_390_IRELATIVE:
+ return &elf_howto_table[(int) R_390_IRELATIVE];
case BFD_RELOC_VTABLE_INHERIT:
return &elf64_s390_vtinherit_howto;
case BFD_RELOC_VTABLE_ENTRY:
and elf64-s390.c has its own copy. */
static void
-elf_s390_info_to_howto (abfd, cache_ptr, dst)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *cache_ptr;
- Elf_Internal_Rela *dst;
+elf_s390_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *cache_ptr,
+ Elf_Internal_Rela *dst)
{
unsigned int r_type = ELF64_R_TYPE(dst->r_info);
switch (r_type)
/* A relocation function which doesn't do anything. */
static bfd_reloc_status_type
-s390_tls_reloc (abfd, reloc_entry, symbol, data, input_section,
- output_bfd, error_message)
- bfd *abfd ATTRIBUTE_UNUSED;
- arelent *reloc_entry;
- asymbol *symbol ATTRIBUTE_UNUSED;
- PTR data ATTRIBUTE_UNUSED;
- asection *input_section;
- bfd *output_bfd;
- char **error_message ATTRIBUTE_UNUSED;
+s390_tls_reloc (bfd *abfd ATTRIBUTE_UNUSED,
+ arelent *reloc_entry,
+ asymbol *symbol ATTRIBUTE_UNUSED,
+ void * data ATTRIBUTE_UNUSED,
+ asection *input_section,
+ bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
{
if (output_bfd)
reloc_entry->address += input_section->output_offset;
/* Handle the large displacement relocs. */
static bfd_reloc_status_type
-s390_elf_ldisp_reloc (abfd, reloc_entry, symbol, data, input_section,
- output_bfd, error_message)
- bfd *abfd;
- arelent *reloc_entry;
- asymbol *symbol;
- PTR data;
- asection *input_section;
- bfd *output_bfd;
- char **error_message ATTRIBUTE_UNUSED;
+s390_elf_ldisp_reloc (bfd *abfd,
+ arelent *reloc_entry,
+ asymbol *symbol,
+ void * data,
+ asection *input_section,
+ bfd *output_bfd,
+ char **error_message ATTRIBUTE_UNUSED)
{
reloc_howto_type *howto = reloc_entry->howto;
bfd_vma relocation;
relocation -= reloc_entry->address;
}
- insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
+ insn = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address);
insn |= (relocation & 0xfff) << 16 | (relocation & 0xff000) >> 4;
bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
}
static bfd_boolean
-elf_s390_is_local_label_name (abfd, name)
- bfd *abfd;
- const char *name;
+elf_s390_is_local_label_name (bfd *abfd, const char *name)
{
if (name[0] == '.' && (name[1] == 'X' || name[1] == 'L'))
return TRUE;
#define GOT_ENTRY_SIZE 8
+#define RELA_ENTRY_SIZE sizeof (Elf64_External_Rela)
+
/* The first three entries in a procedure linkage table are reserved,
and the initial contents are unimportant (we zero them out).
Subsequent entries look like this. See the SVR4 ABI 386
#define GOT_TLS_IE 3
#define GOT_TLS_IE_NLT 3
unsigned char tls_type;
+
+ /* For pointer equality reasons we might need to change the symbol
+ type from STT_GNU_IFUNC to STT_FUNC together with its value and
+ section entry. So after alloc_dynrelocs only these values should
+ be used. In order to check whether a symbol is IFUNC use
+ s390_is_ifunc_symbol_p. */
+ bfd_vma ifunc_resolver_address;
+ asection *ifunc_resolver_section;
};
#define elf_s390_hash_entry(ent) \
((struct elf_s390_link_hash_entry *)(ent))
+/* This structure represents an entry in the local PLT list needed for
+ local IFUNC symbols. */
+struct plt_entry
+{
+ /* The section of the local symbol.
+ Set in relocate_section and used in finish_dynamic_sections. */
+ asection *sec;
+
+ union
+ {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } plt;
+};
+
/* NOTE: Keep this structure in sync with
the one declared in elf32-s390.c. */
struct elf_s390_obj_tdata
{
struct elf_obj_tdata root;
+ /* A local PLT is needed for ifunc symbols. */
+ struct plt_entry *local_plt;
+
/* TLS type for each local got entry. */
char *local_got_tls_type;
};
#define elf_s390_tdata(abfd) \
((struct elf_s390_obj_tdata *) (abfd)->tdata.any)
+#define elf_s390_local_plt(abfd) \
+ (elf_s390_tdata (abfd)->local_plt)
+
#define elf_s390_local_got_tls_type(abfd) \
(elf_s390_tdata (abfd)->local_got_tls_type)
}
static bfd_boolean
-elf_s390_object_p (abfd)
- bfd *abfd;
+elf_s390_object_p (bfd *abfd)
{
/* Set the right machine number for an s390 elf32 file. */
return bfd_default_set_arch_mach (abfd, bfd_arch_s390, bfd_mach_s390_64);
/* Short-cuts to get to dynamic linker sections. */
asection *sdynbss;
asection *srelbss;
+ asection *irelifunc;
union {
bfd_signed_vma refcount;
(elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
== S390_ELF_DATA ? ((struct elf_s390_link_hash_table *) ((p)->hash)) : NULL)
+#define ELF64 1
+#include "elf-s390-common.c"
+
/* Create an entry in an s390 ELF linker hash table. */
static struct bfd_hash_entry *
-link_hash_newfunc (entry, table, string)
- struct bfd_hash_entry *entry;
- struct bfd_hash_table *table;
- const char *string;
+link_hash_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string)
{
/* Allocate the structure if it has not already been allocated by a
subclass. */
eh->dyn_relocs = NULL;
eh->gotplt_refcount = 0;
eh->tls_type = GOT_UNKNOWN;
+ eh->ifunc_resolver_address = 0;
+ eh->ifunc_resolver_section = NULL;
}
return entry;
/* Create an s390 ELF linker hash table. */
static struct bfd_link_hash_table *
-elf_s390_link_hash_table_create (abfd)
- bfd *abfd;
+elf_s390_link_hash_table_create (bfd *abfd)
{
struct elf_s390_link_hash_table *ret;
bfd_size_type amt = sizeof (struct elf_s390_link_hash_table);
- ret = (struct elf_s390_link_hash_table *) bfd_malloc (amt);
+ ret = (struct elf_s390_link_hash_table *) bfd_zmalloc (amt);
if (ret == NULL)
return NULL;
return NULL;
}
- ret->elf.sgot = NULL;
- ret->elf.sgotplt = NULL;
- ret->elf.srelgot = NULL;
- ret->elf.splt = NULL;
- ret->elf.srelplt = NULL;
- ret->sdynbss = NULL;
- ret->srelbss = NULL;
- ret->tls_ldm_got.refcount = 0;
- ret->sym_cache.abfd = NULL;
-
return &ret->elf.root;
}
if (htab == NULL)
return FALSE;
- htab->elf.sgot = bfd_get_section_by_name (dynobj, ".got");
- htab->elf.sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
- htab->elf.srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+ htab->elf.sgot = bfd_get_linker_section (dynobj, ".got");
+ htab->elf.sgotplt = bfd_get_linker_section (dynobj, ".got.plt");
+ htab->elf.srelgot = bfd_get_linker_section (dynobj, ".rela.got");
if (!htab->elf.sgot || !htab->elf.sgotplt || !htab->elf.srelgot)
abort ();
return TRUE;
if (!_bfd_elf_create_dynamic_sections (dynobj, info))
return FALSE;
- htab->elf.splt = bfd_get_section_by_name (dynobj, ".plt");
- htab->elf.srelplt = bfd_get_section_by_name (dynobj, ".rela.plt");
- htab->sdynbss = bfd_get_section_by_name (dynobj, ".dynbss");
+ htab->elf.splt = bfd_get_linker_section (dynobj, ".plt");
+ htab->elf.srelplt = bfd_get_linker_section (dynobj, ".rela.plt");
+ htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
if (!info->shared)
- htab->srelbss = bfd_get_section_by_name (dynobj, ".rela.bss");
+ htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss");
if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
|| (!info->shared && !htab->srelbss))
/* Copy the extra info we tack onto an elf_link_hash_entry. */
static void
-elf_s390_copy_indirect_symbol (info, dir, ind)
- struct bfd_link_info *info;
- struct elf_link_hash_entry *dir, *ind;
+elf_s390_copy_indirect_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
{
struct elf_s390_link_hash_entry *edir, *eind;
}
static int
-elf_s390_tls_transition (info, r_type, is_local)
- struct bfd_link_info *info;
- int r_type;
- int is_local;
+elf_s390_tls_transition (struct bfd_link_info *info,
+ int r_type,
+ int is_local)
{
if (info->shared)
return r_type;
unsigned int r_type;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
+ Elf_Internal_Sym *isym;
r_symndx = ELF64_R_SYM (rel->r_info);
}
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;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ {
+ struct plt_entry *plt;
+
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+
+ if (!s390_elf_create_ifunc_sections (htab->elf.dynobj, info))
+ return FALSE;
+
+ if (local_got_refcounts == NULL)
+ {
+ if (!elf_s390_allocate_local_syminfo (abfd, symtab_hdr))
+ return FALSE;
+ local_got_refcounts = elf_local_got_refcounts (abfd);
+ }
+ plt = elf_s390_local_plt (abfd);
+ plt[r_symndx].plt.refcount++;
+ }
+ h = NULL;
+ }
else
{
h = sym_hashes[r_symndx - symtab_hdr->sh_info];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ /* PR15323, ref flags aren't set for references in the same
+ object. */
+ h->root.non_ir_ref = 1;
}
/* Create got section and local_got_refcounts array if they
if (h == NULL
&& local_got_refcounts == NULL)
{
- bfd_size_type size;
-
- size = symtab_hdr->sh_info;
- size *= (sizeof (bfd_signed_vma) + sizeof(char));
- local_got_refcounts = ((bfd_signed_vma *)
- bfd_zalloc (abfd, size));
- if (local_got_refcounts == NULL)
+ if (!elf_s390_allocate_local_syminfo (abfd, symtab_hdr))
return FALSE;
- elf_local_got_refcounts (abfd) = local_got_refcounts;
- elf_s390_local_got_tls_type (abfd)
- = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+ local_got_refcounts = elf_local_got_refcounts (abfd);
}
+
/* Fall through. */
case R_390_GOTOFF16:
case R_390_GOTOFF32:
}
}
+ if (h != NULL)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (!s390_elf_create_ifunc_sections (htab->elf.dynobj, info))
+ return FALSE;
+
+ /* Make sure an IFUNC symbol defined in a non-shared object
+ always gets a PLT slot. */
+ if (s390_is_ifunc_symbol_p (h) && h->def_regular)
+ {
+ /* The symbol is called by the dynamic loader in order
+ to resolve the relocation. So it is in fact also
+ referenced. */
+ h->ref_regular = 1;
+ h->needs_plt = 1;
+ }
+ }
+
switch (r_type)
{
case R_390_GOTOFF16:
case R_390_GOTOFF64:
case R_390_GOTPC:
case R_390_GOTPCDBL:
- /* Got is created, nothing to be done. */
+ /* These relocs do not need a GOT slot. They just load the
+ GOT pointer itself or address something else relative to
+ the GOT. Since the GOT pointer has been set up above we
+ are done. */
break;
+ case R_390_PLT12DBL:
case R_390_PLT16DBL:
+ case R_390_PLT24DBL:
case R_390_PLT32:
case R_390_PLT32DBL:
case R_390_PLT64:
/* Fall through */
case R_390_TLS_LE64:
+ /* For static linking and executables this reloc will be
+ calculated at linktime otherwise a TLS_TPOFF runtime
+ reloc will be generated. */
+ if (r_type == R_390_TLS_LE64 && info->pie)
+ break;
+
if (!info->shared)
break;
info->flags |= DF_STATIC_TLS;
case R_390_16:
case R_390_32:
case R_390_64:
+ case R_390_PC12DBL:
case R_390_PC16:
case R_390_PC16DBL:
+ case R_390_PC24DBL:
case R_390_PC32:
case R_390_PC32DBL:
case R_390_PC64:
- if (h != NULL && !info->shared)
+ if (h != NULL)
{
/* If this reloc is in a read-only section, we might
need a copy reloc. We can't check reliably at this
adjust_dynamic_symbol. */
h->non_got_ref = 1;
- /* We may need a .plt entry if the function this reloc
- refers to is in a shared lib. */
- h->plt.refcount += 1;
+ if (!info->shared)
+ {
+ /* We may need a .plt entry if the function this reloc
+ refers to is in a shared lib. */
+ h->plt.refcount += 1;
+ }
}
/* If we are creating a shared library, and this is a reloc
if ((info->shared
&& (sec->flags & SEC_ALLOC) != 0
&& ((ELF64_R_TYPE (rel->r_info) != R_390_PC16
+ && ELF64_R_TYPE (rel->r_info) != R_390_PC12DBL
&& ELF64_R_TYPE (rel->r_info) != R_390_PC16DBL
+ && ELF64_R_TYPE (rel->r_info) != R_390_PC24DBL
&& ELF64_R_TYPE (rel->r_info) != R_390_PC32
&& ELF64_R_TYPE (rel->r_info) != R_390_PC32DBL
&& ELF64_R_TYPE (rel->r_info) != R_390_PC64)
easily. Oh well. */
asection *s;
void *vpp;
- Elf_Internal_Sym *isym;
isym = bfd_sym_from_r_symndx (&htab->sym_cache,
abfd, r_symndx);
p->count += 1;
if (ELF64_R_TYPE (rel->r_info) == R_390_PC16
+ || ELF64_R_TYPE (rel->r_info) == R_390_PC12DBL
+ || ELF64_R_TYPE (rel->r_info) == R_390_PC16DBL
|| ELF64_R_TYPE (rel->r_info) == R_390_PC16DBL
|| ELF64_R_TYPE (rel->r_info) == R_390_PC32
|| ELF64_R_TYPE (rel->r_info) == R_390_PC32DBL
break;
}
}
+ else
+ {
+ Elf_Internal_Sym *isym;
+
+ /* A local symbol. */
+ 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)
+ {
+ struct plt_entry *plt = elf_s390_local_plt (abfd);
+ if (plt[r_symndx].plt.refcount > 0)
+ plt[r_symndx].plt.refcount--;
+ }
+ }
r_type = ELF64_R_TYPE (rel->r_info);
r_type = elf_s390_tls_transition (info, r_type, h != NULL);
case R_390_32:
case R_390_64:
case R_390_PC16:
+ case R_390_PC12DBL:
case R_390_PC16DBL:
+ case R_390_PC24DBL:
case R_390_PC32:
case R_390_PC32DBL:
case R_390_PC64:
break;
/* Fall through */
+ case R_390_PLT12DBL:
case R_390_PLT16DBL:
+ case R_390_PLT24DBL:
case R_390_PLT32:
case R_390_PLT32DBL:
case R_390_PLT64:
created (we're only linking static objects). */
static void
-elf_s390_adjust_gotplt (h)
- struct elf_s390_link_hash_entry *h;
+elf_s390_adjust_gotplt (struct elf_s390_link_hash_entry *h)
{
if (h->elf.root.type == bfd_link_hash_warning)
h = (struct elf_s390_link_hash_entry *) h->elf.root.u.i.link;
struct elf_s390_link_hash_table *htab;
asection *s;
+ /* STT_GNU_IFUNC symbol must go through PLT. */
+ if (s390_is_ifunc_symbol_p (h))
+ return TRUE;
+
/* If this is a function, put it in the procedure linkage table. We
will fill in the contents of the procedure linkage table later
(although we could actually do it here). */
s = htab->sdynbss;
- return _bfd_elf_adjust_dynamic_copy (h, s);
+ return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
/* Allocate space in .plt, .got and associated reloc sections for
{
struct bfd_link_info *info;
struct elf_s390_link_hash_table *htab;
- struct elf_s390_link_hash_entry *eh;
+ struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry *)h;
struct elf_dyn_relocs *p;
if (h->root.type == bfd_link_hash_indirect)
if (htab == NULL)
return FALSE;
- if (htab->elf.dynamic_sections_created
- && h->plt.refcount > 0)
+ /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
+ here if it is defined and referenced in a non-shared object. */
+ if (s390_is_ifunc_symbol_p (h) && h->def_regular)
+ return s390_elf_allocate_ifunc_dyn_relocs (info, h,
+ &eh->dyn_relocs);
+ else if (htab->elf.dynamic_sections_created
+ && h->plt.refcount > 0)
{
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
else
h->got.offset = (bfd_vma) -1;
- eh = (struct elf_s390_link_hash_entry *) h;
if (eh->dyn_relocs == NULL)
return TRUE;
/* Find any dynamic relocs that apply to read-only sections. */
static bfd_boolean
-readonly_dynrelocs (h, inf)
- struct elf_link_hash_entry *h;
- PTR inf;
+readonly_dynrelocs (struct elf_link_hash_entry *h, void * inf)
{
struct elf_s390_link_hash_entry *eh;
struct elf_dyn_relocs *p;
/* Set the contents of the .interp section to the interpreter. */
if (info->executable)
{
- s = bfd_get_section_by_name (dynobj, ".interp");
+ s = bfd_get_linker_section (dynobj, ".interp");
if (s == NULL)
abort ();
s->size = sizeof ELF_DYNAMIC_INTERPRETER;
/* Set up .got offsets for local syms, and space for local dynamic
relocs. */
- for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
{
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *srela;
+ struct plt_entry *local_plt;
+ unsigned int i;
if (! is_s390_elf (ibfd))
continue;
else
*local_got = (bfd_vma) -1;
}
+
+ local_plt = elf_s390_local_plt (ibfd);
+ for (i = 0; i < symtab_hdr->sh_info; i++)
+ {
+ if (local_plt[i].plt.refcount > 0)
+ {
+ local_plt[i].plt.offset = htab->elf.iplt->size;
+ htab->elf.iplt->size += PLT_ENTRY_SIZE;
+ htab->elf.igotplt->size += GOT_ENTRY_SIZE;
+ htab->elf.irelplt->size += sizeof (Elf64_External_Rela);
+ }
+ else
+ local_plt[i].plt.offset = (bfd_vma) -1;
+ }
}
if (htab->tls_ldm_got.refcount > 0)
/* Allocate global sym .plt and .got entries, and space for global
sym dynamic relocs. */
- elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info);
+ elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
/* We now have determined the sizes of the various dynamic sections.
Allocate memory for them. */
if (s == htab->elf.splt
|| s == htab->elf.sgot
|| s == htab->elf.sgotplt
- || s == htab->sdynbss)
+ || s == htab->sdynbss
+ || s == htab->elf.iplt
+ || s == htab->elf.igotplt
+ || s == htab->irelifunc)
{
/* Strip this section if we don't need it; see the
comment below. */
then we need a DT_TEXTREL entry. */
if ((info->flags & DF_TEXTREL) == 0)
elf_link_hash_traverse (&htab->elf, readonly_dynrelocs,
- (PTR) info);
+ info);
if ((info->flags & DF_TEXTREL) != 0)
{
This is PT_TLS segment p_vaddr. */
static bfd_vma
-dtpoff_base (info)
- struct bfd_link_info *info;
+dtpoff_base (struct bfd_link_info *info)
{
/* If tls_sec is NULL, we should have signalled an error already. */
if (elf_hash_table (info)->tls_sec == NULL)
if STT_TLS virtual address is ADDRESS. */
static bfd_vma
-tpoff (info, address)
- struct bfd_link_info *info;
- bfd_vma address;
+tpoff (struct bfd_link_info *info, bfd_vma address)
{
struct elf_link_hash_table *htab = elf_hash_table (info);
instruction. */
static void
-invalid_tls_insn (input_bfd, input_section, rel)
- bfd *input_bfd;
- asection *input_section;
- Elf_Internal_Rela *rel;
+invalid_tls_insn (bfd *input_bfd,
+ asection *input_section,
+ Elf_Internal_Rela *rel)
{
reloc_howto_type *howto;
bfd_boolean unresolved_reloc;
bfd_reloc_status_type r;
int tls_type;
+ asection *base_got = htab->elf.sgot;
r_type = ELF64_R_TYPE (rel->r_info);
if (r_type == (int) R_390_GNU_VTINHERIT
{
sym = local_syms + r_symndx;
sec = local_sections[r_symndx];
- relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+
+ if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ {
+ struct plt_entry *local_plt = elf_s390_local_plt (input_bfd);
+ if (local_plt == NULL)
+ return FALSE;
+
+ /* Address of the PLT slot. */
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + local_plt[r_symndx].plt.offset);
+
+ switch (r_type)
+ {
+ case R_390_PLTOFF16:
+ case R_390_PLTOFF32:
+ case R_390_PLTOFF64:
+ relocation -= htab->elf.sgot->output_section->vma;
+ break;
+ case R_390_GOTPLT12:
+ case R_390_GOTPLT16:
+ case R_390_GOTPLT20:
+ case R_390_GOTPLT32:
+ case R_390_GOTPLT64:
+ case R_390_GOTPLTENT:
+ case R_390_GOT12:
+ case R_390_GOT16:
+ case R_390_GOT20:
+ case R_390_GOT32:
+ case R_390_GOT64:
+ case R_390_GOTENT:
+ {
+ /* Write the PLT slot address into the GOT slot. */
+ bfd_put_64 (output_bfd, relocation,
+ htab->elf.sgot->contents +
+ local_got_offsets[r_symndx]);
+ relocation = (local_got_offsets[r_symndx] +
+ htab->elf.sgot->output_offset);
+
+ if (r_type == R_390_GOTENT || r_type == R_390_GOTPLTENT)
+ relocation += htab->elf.sgot->output_section->vma;
+ break;
+ }
+ default:
+ break;
+ }
+ /* The output section is needed later in
+ finish_dynamic_section when creating the dynamic
+ relocation. */
+ local_plt[r_symndx].sec = sec;
+ goto do_relocation;
+ }
+ else
+ relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
}
else
{
bfd_boolean warned ATTRIBUTE_UNUSED;
+ bfd_boolean ignored ATTRIBUTE_UNUSED;
RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
r_symndx, symtab_hdr, sym_hashes,
h, sec, relocation,
- unresolved_reloc, warned);
+ unresolved_reloc, warned, ignored);
}
if (sec != NULL && discarded_section (sec))
{
bfd_vma plt_index;
- /* Calc. index no.
- Current offset - size first entry / entry size. */
- plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) /
- PLT_ENTRY_SIZE;
-
- /* Offset in GOT is PLT index plus GOT headers(3) times 4,
- addr & GOT addr. */
- relocation = (plt_index + 3) * GOT_ENTRY_SIZE;
+ if (s390_is_ifunc_symbol_p (h))
+ {
+ plt_index = h->plt.offset / PLT_ENTRY_SIZE;
+ relocation = (plt_index * GOT_ENTRY_SIZE +
+ htab->elf.igotplt->output_offset);
+ if (r_type == R_390_GOTPLTENT)
+ relocation += htab->elf.igotplt->output_section->vma;
+ }
+ else
+ {
+ /* Calc. index no.
+ Current offset - size first entry / entry size. */
+ plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) /
+ PLT_ENTRY_SIZE;
+
+ /* Offset in GOT is PLT index plus GOT headers(3)
+ times 4, addr & GOT addr. */
+ relocation = (plt_index + 3) * GOT_ENTRY_SIZE;
+ if (r_type == R_390_GOTPLTENT)
+ relocation += htab->elf.sgot->output_section->vma;
+ }
unresolved_reloc = FALSE;
-
- if (r_type == R_390_GOTPLTENT)
- relocation += htab->elf.sgot->output_section->vma;
break;
}
/* Fall through. */
case R_390_GOTENT:
/* Relocation is to the entry for this symbol in the global
offset table. */
- if (htab->elf.sgot == NULL)
+ if (base_got == NULL)
abort ();
if (h != NULL)
off = h->got.offset;
dyn = htab->elf.dynamic_sections_created;
- if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
- || (info->shared
- && SYMBOL_REFERENCES_LOCAL (info, h))
- || (ELF_ST_VISIBILITY (h->other)
- && h->root.type == bfd_link_hash_undefweak))
+
+ if (s390_is_ifunc_symbol_p (h))
+ {
+ BFD_ASSERT (h->plt.offset != (bfd_vma) -1);
+ if (off == (bfd_vma)-1)
+ {
+ /* No explicit GOT usage so redirect to the
+ got.iplt slot. */
+ base_got = htab->elf.igotplt;
+ off = h->plt.offset / PLT_ENTRY_SIZE * GOT_ENTRY_SIZE;
+ }
+ else
+ {
+ /* Explicit GOT slots must contain the address
+ of the PLT slot. This will be handled in
+ finish_dynamic_symbol. */
+ }
+ }
+ else if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ || (info->shared
+ && SYMBOL_REFERENCES_LOCAL (info, h))
+ || (ELF_ST_VISIBILITY (h->other)
+ && h->root.type == bfd_link_hash_undefweak))
{
/* This is actually a static link, or it is a
-Bsymbolic link and the symbol is defined
else
{
bfd_put_64 (output_bfd, relocation,
- htab->elf.sgot->contents + off);
+ base_got->contents + off);
h->got.offset |= 1;
}
+
+ if ((h->def_regular
+ && info->shared
+ && SYMBOL_REFERENCES_LOCAL (info, h))
+ /* lgrl rx,sym@GOTENT -> larl rx, sym */
+ && ((r_type == R_390_GOTENT
+ && (bfd_get_16 (input_bfd,
+ contents + rel->r_offset - 2)
+ & 0xff0f) == 0xc408)
+ /* lg rx, sym@GOT(r12) -> larl rx, sym */
+ || (r_type == R_390_GOT20
+ && (bfd_get_32 (input_bfd,
+ contents + rel->r_offset - 2)
+ & 0xff00f000) == 0xe300c000
+ && bfd_get_8 (input_bfd,
+ contents + rel->r_offset + 3) == 0x04)))
+
+ {
+ unsigned short new_insn =
+ (0xc000 | (bfd_get_8 (input_bfd,
+ contents + rel->r_offset - 1) & 0xf0));
+ bfd_put_16 (output_bfd, new_insn,
+ contents + rel->r_offset - 2);
+ r_type = R_390_PC32DBL;
+ rel->r_addend = 2;
+ howto = elf_howto_table + r_type;
+ relocation = h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset;
+ goto do_relocation;
+ }
}
else
unresolved_reloc = FALSE;
if (off >= (bfd_vma) -2)
abort ();
- relocation = htab->elf.sgot->output_offset + off;
+ relocation = base_got->output_offset + off;
/* For @GOTENT the relocation is against the offset between
the instruction and the symbols entry in the GOT and not
add the vma of the GOT to get the correct value. */
if ( r_type == R_390_GOTENT
|| r_type == R_390_GOTPLTENT)
- relocation += htab->elf.sgot->output_section->vma;
+ relocation += base_got->output_section->vma;
break;
unresolved_reloc = FALSE;
break;
+ case R_390_PLT12DBL:
case R_390_PLT16DBL:
+ case R_390_PLT24DBL:
case R_390_PLT32:
case R_390_PLT32DBL:
case R_390_PLT64:
break;
if (h->plt.offset == (bfd_vma) -1
- || htab->elf.splt == NULL)
+ || (htab->elf.splt == NULL && !s390_is_ifunc_symbol_p (h)))
{
/* We didn't make a PLT entry for this symbol. This
happens when statically linking PIC code, or when
using -Bsymbolic. */
break;
}
-
- relocation = (htab->elf.splt->output_section->vma
- + htab->elf.splt->output_offset
- + h->plt.offset);
+ if (s390_is_ifunc_symbol_p (h))
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h->plt.offset);
+ else
+ relocation = (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ + h->plt.offset);
unresolved_reloc = FALSE;
break;
/* For local symbols or if we didn't make a PLT entry for
this symbol resolve the symbol directly. */
- if ( h == NULL
+ if (h == NULL
|| h->plt.offset == (bfd_vma) -1
- || htab->elf.splt == NULL)
+ || (htab->elf.splt == NULL && !s390_is_ifunc_symbol_p (h)))
{
relocation -= htab->elf.sgot->output_section->vma;
break;
}
- relocation = (htab->elf.splt->output_section->vma
- + htab->elf.splt->output_offset
- + h->plt.offset
- - htab->elf.sgot->output_section->vma);
+ if (s390_is_ifunc_symbol_p (h))
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h->plt.offset
+ - htab->elf.sgot->output_section->vma);
+ else
+ relocation = (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ + h->plt.offset
+ - htab->elf.sgot->output_section->vma);
unresolved_reloc = FALSE;
break;
case R_390_32:
case R_390_64:
case R_390_PC16:
+ case R_390_PC12DBL:
case R_390_PC16DBL:
+ case R_390_PC24DBL:
case R_390_PC32:
case R_390_PC32DBL:
case R_390_PC64:
+
+ if (h != NULL
+ && s390_is_ifunc_symbol_p (h)
+ && h->def_regular)
+ {
+ if (!info->shared || !h->non_got_ref)
+ {
+ /* For a non-shared object STT_GNU_IFUNC symbol must
+ go through PLT. */
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h ->plt.offset);
+ goto do_relocation;
+ }
+ else
+ {
+ /* For shared objects a runtime relocation is needed. */
+
+ Elf_Internal_Rela outrel;
+ asection *sreloc;
+
+ /* Need a dynamic relocation to get the real function
+ address. */
+ outrel.r_offset = _bfd_elf_section_offset (output_bfd,
+ info,
+ input_section,
+ rel->r_offset);
+ if (outrel.r_offset == (bfd_vma) -1
+ || outrel.r_offset == (bfd_vma) -2)
+ abort ();
+
+ outrel.r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+
+ if (h->dynindx == -1
+ || h->forced_local
+ || info->executable)
+ {
+ /* This symbol is resolved locally. */
+ outrel.r_info = ELF64_R_INFO (0, R_390_IRELATIVE);
+ outrel.r_addend = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+ }
+ else
+ {
+ outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
+ outrel.r_addend = 0;
+ }
+
+ sreloc = htab->elf.irelifunc;
+ elf_append_rela (output_bfd, sreloc, &outrel);
+
+ /* If this reloc is against an external symbol, we
+ do not want to fiddle with the addend. Otherwise,
+ we need to include the symbol value so that it
+ becomes an addend for the dynamic reloc. For an
+ internal symbol, we have updated addend. */
+ continue;
+ }
+ }
+
if ((input_section->flags & SEC_ALLOC) == 0)
break;
|| ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|| h->root.type != bfd_link_hash_undefweak)
&& ((r_type != R_390_PC16
+ && r_type != R_390_PC12DBL
&& r_type != R_390_PC16DBL
+ && r_type != R_390_PC24DBL
&& r_type != R_390_PC32
&& r_type != R_390_PC32DBL
&& r_type != R_390_PC64)
else if (h != NULL
&& h->dynindx != -1
&& (r_type == R_390_PC16
+ || r_type == R_390_PC12DBL
|| r_type == R_390_PC16DBL
+ || r_type == R_390_PC24DBL
|| r_type == R_390_PC32
|| r_type == R_390_PC32DBL
|| r_type == R_390_PC64
break;
case R_390_TLS_LE64:
- if (info->shared)
+ if (info->shared && !info->pie)
{
/* Linking a shared library with non-fpic code requires
a R_390_TLS_TPOFF relocation. */
howto->name,
h->root.root.string);
+ do_relocation:
+
+ /* When applying a 24 bit reloc we need to start one byte
+ earlier. Otherwise the 32 bit get/put bfd operations might
+ access a byte after the actual section. */
+ if (r_type == R_390_PC24DBL
+ || r_type == R_390_PLT24DBL)
+ rel->r_offset--;
+
if (r_type == R_390_20
|| r_type == R_390_GOT20
|| r_type == R_390_GOTPLT20
return TRUE;
}
+/* Generate the PLT slots together with the dynamic relocations needed
+ for IFUNC symbols. */
+
+static void
+elf_s390_finish_ifunc_symbol (bfd *output_bfd,
+ struct bfd_link_info *info,
+ struct elf_link_hash_entry *h,
+ struct elf_s390_link_hash_table *htab,
+ bfd_vma plt_offset,
+ bfd_vma resolver_address)
+{
+ bfd_vma plt_index;
+ bfd_vma got_offset;
+ Elf_Internal_Rela rela;
+ bfd_byte *loc;
+ asection *plt, *gotplt, *relplt;
+
+ if (htab->elf.iplt == NULL
+ || htab->elf.igotplt == NULL
+ || htab->elf.irelplt == NULL)
+ abort ();
+
+ /* Index of the PLT slot within iplt section. */
+ plt_index = plt_offset / PLT_ENTRY_SIZE;
+ plt = htab->elf.iplt;
+ /* Offset into the igot.plt section. */
+ got_offset = plt_index * GOT_ENTRY_SIZE;
+ gotplt = htab->elf.igotplt;
+ relplt = htab->elf.irelplt;
+
+ /* Fill in the blueprint of a PLT. */
+ memcpy (plt->contents + plt_offset, elf_s390x_plt_entry,
+ PLT_ENTRY_SIZE);
+
+ /* Fixup the relative address to the GOT entry */
+ bfd_put_32 (output_bfd,
+ (gotplt->output_section->vma +
+ gotplt->output_offset + got_offset
+ - (plt->output_section->vma +
+ plt->output_offset +
+ plt_offset))/2,
+ plt->contents + plt_offset + 2);
+ /* Fixup the relative branch to PLT 0 */
+ bfd_put_32 (output_bfd, - (plt->output_offset +
+ (PLT_ENTRY_SIZE * plt_index) + 22)/2,
+ plt->contents + plt_offset + 24);
+ /* Fixup offset into .rela.plt section. */
+ bfd_put_32 (output_bfd, relplt->output_offset +
+ plt_index * sizeof (Elf64_External_Rela),
+ plt->contents + plt_offset + 28);
+
+ /* Fill in the entry in the global offset table.
+ Points to instruction after GOT offset. */
+ bfd_put_64 (output_bfd,
+ (plt->output_section->vma
+ + plt->output_offset
+ + plt_offset
+ + 14),
+ gotplt->contents + got_offset);
+
+ /* Fill in the entry in the .rela.plt section. */
+ rela.r_offset = (gotplt->output_section->vma
+ + gotplt->output_offset
+ + got_offset);
+
+ if (!h
+ || h->dynindx == -1
+ || ((info->executable
+ || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ && h->def_regular))
+ {
+ /* The symbol can be locally resolved. */
+ rela.r_info = ELF64_R_INFO (0, R_390_IRELATIVE);
+ rela.r_addend = resolver_address;
+ }
+ else
+ {
+ rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
+ rela.r_addend = 0;
+ }
+
+ loc = relplt->contents + plt_index * sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+}
+
+
/* Finish up dynamic symbol handling. We set the contents of various
dynamic sections here. */
Elf_Internal_Sym *sym)
{
struct elf_s390_link_hash_table *htab;
+ struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
htab = elf_s390_hash_table (info);
if (htab == NULL)
/* This symbol has an entry in the procedure linkage table. Set
it up. */
+ if (s390_is_ifunc_symbol_p (h))
+ {
+ /* If we can resolve the IFUNC symbol locally we generate an
+ IRELATIVE reloc. */
+ elf_s390_finish_ifunc_symbol (output_bfd, info, h, htab, h->plt.offset,
+ eh->ifunc_resolver_address +
+ eh->ifunc_resolver_section->output_offset +
+ eh->ifunc_resolver_section->output_section->vma);
+ ;
+ /* Fallthrough. Handling of explicit GOT slots of IFUNC
+ symbols is below. */
+ }
+ else
+ {
+ if (h->dynindx == -1
+ || htab->elf.splt == NULL
+ || htab->elf.sgotplt == NULL
+ || htab->elf.srelplt == NULL)
+ abort ();
- if (h->dynindx == -1
- || htab->elf.splt == NULL
- || htab->elf.sgotplt == NULL
- || htab->elf.srelplt == NULL)
- abort ();
+ /* Calc. index no.
+ Current offset - size first entry / entry size. */
+ plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) / PLT_ENTRY_SIZE;
- /* Calc. index no.
- Current offset - size first entry / entry size. */
- plt_index = (h->plt.offset - PLT_FIRST_ENTRY_SIZE) / PLT_ENTRY_SIZE;
-
- /* Offset in GOT is PLT index plus GOT headers(3) times 8,
- addr & GOT addr. */
- got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
-
- /* Fill in the blueprint of a PLT. */
- memcpy (htab->elf.splt->contents + h->plt.offset, elf_s390x_plt_entry,
- PLT_ENTRY_SIZE);
-
- /* Fixup the relative address to the GOT entry */
- bfd_put_32 (output_bfd,
- (htab->elf.sgotplt->output_section->vma +
- htab->elf.sgotplt->output_offset + got_offset
- - (htab->elf.splt->output_section->vma + h->plt.offset))/2,
- htab->elf.splt->contents + h->plt.offset + 2);
- /* Fixup the relative branch to PLT 0 */
- bfd_put_32 (output_bfd, - (PLT_FIRST_ENTRY_SIZE +
- (PLT_ENTRY_SIZE * plt_index) + 22)/2,
- htab->elf.splt->contents + h->plt.offset + 24);
- /* Fixup offset into .rela.plt section. */
- bfd_put_32 (output_bfd, plt_index * sizeof (Elf64_External_Rela),
- htab->elf.splt->contents + h->plt.offset + 28);
-
- /* Fill in the entry in the global offset table.
- Points to instruction after GOT offset. */
- bfd_put_64 (output_bfd,
- (htab->elf.splt->output_section->vma
- + htab->elf.splt->output_offset
- + h->plt.offset
- + 14),
- htab->elf.sgotplt->contents + got_offset);
-
- /* Fill in the entry in the .rela.plt section. */
- rela.r_offset = (htab->elf.sgotplt->output_section->vma
- + htab->elf.sgotplt->output_offset
- + got_offset);
- rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
- rela.r_addend = 0;
- loc = htab->elf.srelplt->contents + plt_index * sizeof (Elf64_External_Rela);
- bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+ /* Offset in GOT is PLT index plus GOT headers(3) times 8,
+ addr & GOT addr. */
+ got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
- if (!h->def_regular)
- {
- /* Mark the symbol as undefined, rather than as defined in
- the .plt section. Leave the value alone. This is a clue
- for the dynamic linker, to make function pointer
- comparisons work between an application and shared
- library. */
- sym->st_shndx = SHN_UNDEF;
+ /* Fill in the blueprint of a PLT. */
+ memcpy (htab->elf.splt->contents + h->plt.offset, elf_s390x_plt_entry,
+ PLT_ENTRY_SIZE);
+
+ /* Fixup the relative address to the GOT entry */
+ bfd_put_32 (output_bfd,
+ (htab->elf.sgotplt->output_section->vma +
+ htab->elf.sgotplt->output_offset + got_offset
+ - (htab->elf.splt->output_section->vma +
+ htab->elf.splt->output_offset +
+ h->plt.offset))/2,
+ htab->elf.splt->contents + h->plt.offset + 2);
+ /* Fixup the relative branch to PLT 0 */
+ bfd_put_32 (output_bfd, - (PLT_FIRST_ENTRY_SIZE +
+ (PLT_ENTRY_SIZE * plt_index) + 22)/2,
+ htab->elf.splt->contents + h->plt.offset + 24);
+ /* Fixup offset into .rela.plt section. */
+ bfd_put_32 (output_bfd, plt_index * sizeof (Elf64_External_Rela),
+ htab->elf.splt->contents + h->plt.offset + 28);
+
+ /* Fill in the entry in the global offset table.
+ Points to instruction after GOT offset. */
+ bfd_put_64 (output_bfd,
+ (htab->elf.splt->output_section->vma
+ + htab->elf.splt->output_offset
+ + h->plt.offset
+ + 14),
+ htab->elf.sgotplt->contents + got_offset);
+
+ /* Fill in the entry in the .rela.plt section. */
+ rela.r_offset = (htab->elf.sgotplt->output_section->vma
+ + htab->elf.sgotplt->output_offset
+ + got_offset);
+ rela.r_info = ELF64_R_INFO (h->dynindx, R_390_JMP_SLOT);
+ rela.r_addend = 0;
+ loc = htab->elf.srelplt->contents + plt_index *
+ sizeof (Elf64_External_Rela);
+ bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
+
+ if (!h->def_regular)
+ {
+ /* Mark the symbol as undefined, rather than as defined in
+ the .plt section. Leave the value alone. This is a clue
+ for the dynamic linker, to make function pointer
+ comparisons work between an application and shared
+ library. */
+ sym->st_shndx = SHN_UNDEF;
+ }
}
}
+ htab->elf.sgot->output_offset
+ (h->got.offset &~ (bfd_vma) 1));
- /* If this is a static link, or it is a -Bsymbolic link and the
- symbol is defined locally or was forced to be local because
- of a version file, we just want to emit a RELATIVE reloc.
- The entry in the global offset table will already have been
- initialized in the relocate_section function. */
- if (info->shared
+ if (h->def_regular && s390_is_ifunc_symbol_p (h))
+ {
+ if (info->shared)
+ {
+ /* An explicit GOT slot usage needs GLOB_DAT. If the
+ symbol references local the implicit got.iplt slot
+ will be used and the IRELATIVE reloc has been created
+ above. */
+ goto do_glob_dat;
+ }
+ else
+ {
+ /* For non-shared objects explicit GOT slots must be
+ filled with the PLT slot address for pointer
+ equality reasons. */
+ bfd_put_64 (output_bfd, (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h->plt.offset),
+ htab->elf.sgot->contents + h->got.offset);
+ return TRUE;
+ }
+ }
+ else if (info->shared
&& SYMBOL_REFERENCES_LOCAL (info, h))
{
+ /* If this is a static link, or it is a -Bsymbolic link and
+ the symbol is defined locally or was forced to be local
+ because of a version file, we just want to emit a
+ RELATIVE reloc. The entry in the global offset table
+ will already have been initialized in the
+ relocate_section function. */
if (!h->def_regular)
return FALSE;
BFD_ASSERT((h->got.offset & 1) != 0);
else
{
BFD_ASSERT((h->got.offset & 1) == 0);
+do_glob_dat:
bfd_put_64 (output_bfd, (bfd_vma) 0, htab->elf.sgot->contents + h->got.offset);
rela.r_info = ELF64_R_INFO (h->dynindx, R_390_GLOB_DAT);
rela.r_addend = 0;
}
/* Mark some specially defined symbols as absolute. */
- if (strcmp (h->root.root.string, "_DYNAMIC") == 0
+ if (h == htab->elf.hdynamic
|| h == htab->elf.hgot
|| h == htab->elf.hplt)
sym->st_shndx = SHN_ABS;
dynamic linker, before writing them out. */
static enum elf_reloc_type_class
-elf_s390_reloc_type_class (rela)
- const Elf_Internal_Rela *rela;
+elf_s390_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ const asection *rel_sec ATTRIBUTE_UNUSED,
+ const Elf_Internal_Rela *rela)
{
switch ((int) ELF64_R_TYPE (rela->r_info))
{
struct elf_s390_link_hash_table *htab;
bfd *dynobj;
asection *sdyn;
+ bfd *ibfd;
+ unsigned int i;
htab = elf_s390_hash_table (info);
if (htab == NULL)
return FALSE;
dynobj = htab->elf.dynobj;
- sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+ sdyn = bfd_get_linker_section (dynobj, ".dynamic");
if (htab->elf.dynamic_sections_created)
{
- htab->elf.splt->output_section->vma - 6)/2,
htab->elf.splt->contents + 8);
}
- elf_section_data (htab->elf.splt->output_section)
- ->this_hdr.sh_entsize = PLT_ENTRY_SIZE;
+ if (elf_section_data (htab->elf.splt->output_section) != NULL)
+ elf_section_data (htab->elf.splt->output_section)->this_hdr.sh_entsize
+ = PLT_ENTRY_SIZE;
}
if (htab->elf.sgotplt)
elf_section_data (htab->elf.sgot->output_section)
->this_hdr.sh_entsize = 8;
}
+
+ /* Finish dynamic symbol for local IFUNC symbols. */
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+ {
+ struct plt_entry *local_plt;
+ Elf_Internal_Sym *isym;
+ Elf_Internal_Shdr *symtab_hdr;
+
+ symtab_hdr = &elf_symtab_hdr (ibfd);
+
+ local_plt = elf_s390_local_plt (ibfd);
+ if (local_plt != NULL)
+ for (i = 0; i < symtab_hdr->sh_info; i++)
+ {
+ if (local_plt[i].plt.offset != (bfd_vma) -1)
+ {
+ asection *sec = local_plt[i].sec;
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache, ibfd, i);
+ if (isym == NULL)
+ return FALSE;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ elf_s390_finish_ifunc_symbol (output_bfd, info, NULL, htab,
+ local_plt[i].plt.offset,
+ isym->st_value
+ + sec->output_section->vma
+ + sec->output_offset);
+
+ }
+ }
+ }
+
return TRUE;
}
return plt->vma + PLT_FIRST_ENTRY_SIZE + i * PLT_ENTRY_SIZE;
}
-
/* Why was the hash table entry size definition changed from
ARCH_SIZE/8 to 4? This breaks the 64 bit dynamic linker and
this is the only reason for the s390_elf64_size_info structure. */
bfd_elf64_swap_reloca_out
};
-#define TARGET_BIG_SYM bfd_elf64_s390_vec
+#define TARGET_BIG_SYM s390_elf64_vec
#define TARGET_BIG_NAME "elf64-s390"
#define ELF_ARCH bfd_arch_s390
#define ELF_TARGET_ID S390_ELF_DATA
#define elf_backend_relocate_section elf_s390_relocate_section
#define elf_backend_size_dynamic_sections elf_s390_size_dynamic_sections
#define elf_backend_init_index_section _bfd_elf_init_1_index_section
-#define elf_backend_reloc_type_class elf_s390_reloc_type_class
#define elf_backend_plt_sym_val elf_s390_plt_sym_val
+#define elf_backend_add_symbol_hook elf_s390_add_symbol_hook
#define bfd_elf64_mkobject elf_s390_mkobject
#define elf_backend_object_p elf_s390_object_p
+/* Enable ELF64 archive functions. */
+#define bfd_elf64_archive_functions
+extern bfd_boolean bfd_elf64_archive_slurp_armap (bfd *);
+extern bfd_boolean bfd_elf64_archive_write_armap (bfd *, unsigned int, struct orl *, unsigned int, int);
+
+#define bfd_elf64_archive_slurp_extended_name_table _bfd_archive_coff_slurp_extended_name_table
+#define bfd_elf64_archive_construct_extended_name_table _bfd_archive_coff_construct_extended_name_table
+#define bfd_elf64_archive_truncate_arname _bfd_archive_coff_truncate_arname
+#define bfd_elf64_archive_read_ar_hdr _bfd_archive_coff_read_ar_hdr
+#define bfd_elf64_archive_write_ar_hdr _bfd_archive_coff_write_ar_hdr
+#define bfd_elf64_archive_openr_next_archived_file _bfd_archive_coff_openr_next_archived_file
+#define bfd_elf64_archive_get_elt_at_index _bfd_archive_coff_get_elt_at_index
+#define bfd_elf64_archive_generic_stat_arch_elt _bfd_archive_coff_generic_stat_arch_elt
+#define bfd_elf64_archive_update_armap_timestamp _bfd_archive_coff_update_armap_timestamp
+
#include "elf64-target.h"