/* IBM S/390-specific support for 32-bit ELF
- Copyright (C) 2000-2015 Free Software Foundation, Inc.
+ Copyright (C) 2000-2016 Free Software Foundation, Inc.
Contributed by Carl B. Pedersen and Martin Schwidefsky.
This file is part of BFD, the Binary File Descriptor library.
#include "libbfd.h"
#include "elf-bfd.h"
#include "elf/s390.h"
+#include <stdarg.h>
static bfd_reloc_status_type
s390_tls_reloc (bfd *, arelent *, asymbol *, void *,
default:
if (r_type >= sizeof (elf_howto_table) / sizeof (elf_howto_table[0]))
{
- (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
- abfd, (int) r_type);
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%B: invalid relocation type %d"),
+ abfd, (int) r_type);
r_type = R_390_NONE;
}
cache_ptr->howto = &elf_howto_table[r_type];
struct elf_link_hash_table elf;
/* Short-cuts to get to dynamic linker sections. */
- asection *sdynbss;
- asection *srelbss;
asection *irelifunc;
union
return &ret->elf.root;
}
-/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up
- shortcuts to them in our hash table. */
-
-static bfd_boolean
-create_got_section (bfd *dynobj, struct bfd_link_info *info)
-{
- struct elf_s390_link_hash_table *htab;
-
- if (! _bfd_elf_create_got_section (dynobj, info))
- return FALSE;
-
- htab = elf_s390_hash_table (info);
- 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;
-}
-
-/* Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and
- .rela.bss sections in DYNOBJ, and set up shortcuts to them in our
- hash table. */
-
-static bfd_boolean
-elf_s390_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
-{
- struct elf_s390_link_hash_table *htab;
-
- htab = elf_s390_hash_table (info);
- if (!htab->elf.sgot && !create_got_section (dynobj, info))
- return FALSE;
-
- if (!_bfd_elf_create_dynamic_sections (dynobj, info))
- return FALSE;
-
- 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 (!bfd_link_pic (info))
- htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss");
-
- if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
- || (!bfd_link_pic (info) && !htab->srelbss))
- abort ();
-
- return TRUE;
-}
-
/* Copy the extra info we tack onto an elf_link_hash_entry. */
static void
if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
{
- (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
- abfd, r_symndx);
+ /* xgettext:c-format */
+ _bfd_error_handler (_("%B: bad symbol index: %d"),
+ abfd, r_symndx);
return FALSE;
}
{
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
- if (!create_got_section (htab->elf.dynobj, info))
+ if (!_bfd_elf_create_got_section (htab->elf.dynobj, info))
return FALSE;
}
}
}
switch (r_type)
{
- case R_390_GOTOFF16:
- case R_390_GOTOFF32:
case R_390_GOTPC:
case R_390_GOTPCDBL:
/* These relocs do not need a GOT slot. They just load the
the GOT. Since the GOT pointer has been set up above we
are done. */
break;
+ case R_390_GOTOFF16:
+ case R_390_GOTOFF32:
+ if (h == NULL || !s390_is_ifunc_symbol_p (h) || !h->def_regular)
+ break;
+ /* Fall through. */
case R_390_PLT12DBL:
case R_390_PLT16DBL:
{
if (old_tls_type == GOT_NORMAL || tls_type == GOT_NORMAL)
{
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B: `%s' accessed both as normal and thread local symbol"),
abfd, h->root.root.string);
return FALSE;
case R_390_PC24DBL:
case R_390_PC32DBL:
case R_390_PC32:
- if (h != NULL)
+ if (h != NULL && bfd_link_executable (info))
{
/* If this reloc is in a read-only section, we might
need a copy reloc. We can't check reliably at this
elf_s390_hash_table (info)->tls_ldm_got.refcount -= 1;
break;
+ case R_390_GOTOFF16:
+ case R_390_GOTOFF32:
+ if (h != NULL && s390_is_ifunc_symbol_p (h) && h->def_regular)
+ {
+ h->plt.refcount--;
+ break;
+ }
+
+ case R_390_GOTPC:
+ case R_390_GOTPCDBL:
+ break;
+
case R_390_TLS_GD32:
case R_390_TLS_IE32:
case R_390_TLS_GOTIE12:
case R_390_GOT16:
case R_390_GOT20:
case R_390_GOT32:
- case R_390_GOTOFF16:
- case R_390_GOTOFF32:
- case R_390_GOTPC:
- case R_390_GOTPCDBL:
case R_390_GOTENT:
if (h != NULL)
{
/* STT_GNU_IFUNC symbol must go through PLT. */
if (s390_is_ifunc_symbol_p (h))
- return TRUE;
+ {
+ /* All local STT_GNU_IFUNC references must be treated as local
+ calls via local PLT. */
+ if (h->ref_regular && SYMBOL_CALLS_LOCAL (info, h))
+ {
+ bfd_size_type pc_count = 0, count = 0;
+ struct elf_dyn_relocs **pp;
+ struct elf_s390_link_hash_entry *eh;
+ struct elf_dyn_relocs *p;
+
+ eh = (struct elf_s390_link_hash_entry *) h;
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ pc_count += p->pc_count;
+ p->count -= p->pc_count;
+ p->pc_count = 0;
+ count += p->count;
+ if (p->count == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+
+ if (pc_count || count)
+ {
+ h->needs_plt = 1;
+ h->non_got_ref = 1;
+ if (h->plt.refcount <= 0)
+ h->plt.refcount = 1;
+ else
+ h->plt.refcount += 1;
+ }
+ }
+
+ if (h->plt.refcount <= 0)
+ {
+ h->plt.offset = (bfd_vma) -1;
+ h->needs_plt = 0;
+ }
+ 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
runtime process image. */
if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
{
- htab->srelbss->size += sizeof (Elf32_External_Rela);
+ htab->elf.srelbss->size += sizeof (Elf32_External_Rela);
h->needs_copy = 1;
}
- s = htab->sdynbss;
+ s = htab->elf.sdynbss;
return _bfd_elf_adjust_dynamic_copy (info, h, s);
}
/* 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);
+ return s390_elf_allocate_ifunc_dyn_relocs (info, h);
else if (htab->elf.dynamic_sections_created
&& h->plt.refcount > 0)
{
h->got.offset = (bfd_vma) -1;
}
else if (h->got.refcount > 0)
- {
+ {
asection *s;
bfd_boolean dyn;
int tls_type = elf_s390_hash_entry(h)->tls_type;
if (htab->elf.dynamic_sections_created)
{
/* Set the contents of the .interp section to the interpreter. */
- if (bfd_link_executable (info))
+ if (bfd_link_executable (info) && !info->nointerp)
{
s = bfd_get_linker_section (dynobj, ".interp");
if (s == NULL)
if (s == htab->elf.splt
|| s == htab->elf.sgot
|| s == htab->elf.sgotplt
- || s == htab->sdynbss
+ || s == htab->elf.sdynbss
|| s == htab->elf.iplt
|| s == htab->elf.igotplt
|| s == htab->irelifunc)
reloc_howto_type *howto;
howto = elf_howto_table + ELF32_R_TYPE (rel->r_info);
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B(%A+0x%lx): invalid instruction for TLS relocation %s"),
input_bfd,
input_section,
/* Relocation is relative to the start of the global offset
table. */
+ if (h != NULL
+ && s390_is_ifunc_symbol_p (h)
+ && h->def_regular
+ && !bfd_link_executable (info))
+ {
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h->plt.offset
+ - htab->elf.sgot->output_section->vma);
+ goto do_relocation;
+ }
+
/* Note that sgot->output_offset is not involved in this
calculation. We always want the start of .got. If we
defined _GLOBAL_OFFSET_TABLE in a different way, as is
unresolved_reloc = FALSE;
break;
- case R_390_8:
- case R_390_16:
- case R_390_32:
case R_390_PC16:
case R_390_PC12DBL:
case R_390_PC16DBL:
case R_390_PC24DBL:
case R_390_PC32DBL:
case R_390_PC32:
+ if (h != NULL
+ && s390_is_ifunc_symbol_p (h)
+ && h->def_regular
+ && !bfd_link_executable (info))
+ {
+ /* This will not work our if the function does not
+ happen to set up the GOT pointer for some other
+ reason. 31 bit PLT entries require r12 to hold the
+ GOT pointer.
+ FIXME: Implement an errorcheck.
+ NOTE: It will work when brasl is not available
+ (e.g. with -m31 -march=g5) since a local function
+ call then does use GOTOFF which implies r12 being set
+ up. */
+ relocation = (htab->elf.iplt->output_section->vma
+ + htab->elf.iplt->output_offset
+ + h ->plt.offset);
+ goto do_relocation;
+ }
+ /* Fall through. */
+
+ case R_390_8:
+ case R_390_16:
+ case R_390_32:
if (h != NULL
&& s390_is_ifunc_symbol_p (h)
&& h->def_regular)
&& h->def_dynamic)
&& _bfd_elf_section_offset (output_bfd, info, input_section,
rel->r_offset) != (bfd_vma) -1)
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
input_bfd,
input_section,
}
if (r == bfd_reloc_overflow)
- {
-
- if (! ((*info->callbacks->reloc_overflow)
- (info, (h ? &h->root : NULL), name, howto->name,
- (bfd_vma) 0, input_bfd, input_section,
- rel->r_offset)))
- return FALSE;
- }
+ (*info->callbacks->reloc_overflow)
+ (info, (h ? &h->root : NULL), name, howto->name,
+ (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
else
{
- (*_bfd_error_handler)
+ _bfd_error_handler
+ /* xgettext:c-format */
(_("%B(%A+0x%lx): reloc against `%s': error %d"),
input_bfd, input_section,
(long) rel->r_offset, name, (int) r);
/* S390 uses halfwords for relative branch calc! */
relative_offset = - (plt->output_offset +
(PLT_ENTRY_SIZE * iplt_index) + 18) / 2;
-/* If offset is > 32768, branch to a previous branch
- 390 can only handle +-64 K jumps. */
+ /* If offset is > 32768, branch to a previous branch
+ 390 can only handle +-64 K jumps. */
if ( -32768 > (int) relative_offset )
relative_offset
= -(unsigned) (((65536 / PLT_ENTRY_SIZE - 1) * PLT_ENTRY_SIZE) / 2);
/* This symbol has an entry in the procedure linkage table. Set
it up. */
- if (s390_is_ifunc_symbol_p (h))
+ if (s390_is_ifunc_symbol_p (h) && h->def_regular)
{
- /* 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. */
+ 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);
+ /* Do not return yet. Handling of explicit GOT slots of
+ IFUNC symbols is below. */
}
else
{
/* Put in the GOT offset as displacement value. The 0xc000
value comes from the first word of the plt entry. Look
- at the elf_s390_plt_pic16_entry content. */
+ at the elf_s390_plt_pic12_entry content. */
bfd_put_16 (output_bfd, (bfd_vma)0xc000 | got_offset,
htab->elf.splt->contents + h->plt.offset + 2);
}
}
else if (bfd_link_pic (info)
- && SYMBOL_REFERENCES_LOCAL (info, h))
+ && 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
if (h->dynindx == -1
|| (h->root.type != bfd_link_hash_defined
&& h->root.type != bfd_link_hash_defweak)
- || htab->srelbss == NULL)
+ || htab->elf.srelbss == NULL)
abort ();
rela.r_offset = (h->root.u.def.value
+ h->root.u.def.section->output_offset);
rela.r_info = ELF32_R_INFO (h->dynindx, R_390_COPY);
rela.r_addend = 0;
- loc = htab->srelbss->contents;
- loc += htab->srelbss->reloc_count++ * sizeof (Elf32_External_Rela);
+ loc = htab->elf.srelbss->contents;
+ loc += htab->elf.srelbss->reloc_count++ * sizeof (Elf32_External_Rela);
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
}
const asection *rel_sec ATTRIBUTE_UNUSED,
const Elf_Internal_Rela *rela)
{
+ bfd *abfd = info->output_bfd;
+ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+ struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
+ unsigned long r_symndx = ELF32_R_SYM (rela->r_info);
+ Elf_Internal_Sym sym;
+
+ if (htab->elf.dynsym == NULL
+ || !bed->s->swap_symbol_in (abfd,
+ (htab->elf.dynsym->contents
+ + r_symndx * bed->s->sizeof_sym),
+ 0, &sym))
+ abort ();
+
+ /* Check relocation against STT_GNU_IFUNC symbol. */
+ if (ELF_ST_TYPE (sym.st_info) == STT_GNU_IFUNC)
+ return reloc_class_ifunc;
+
switch ((int) ELF32_R_TYPE (rela->r_info))
{
case R_390_RELATIVE:
continue;
case DT_PLTGOT:
- dyn.d_un.d_ptr = htab->elf.sgot->output_section->vma;
+ s = htab->elf.sgotplt;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
break;
case DT_JMPREL:
- dyn.d_un.d_ptr = htab->elf.srelplt->output_section->vma;
+ s = htab->elf.srelplt;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
break;
case DT_PLTRELSZ:
- s = htab->elf.srelplt->output_section;
- dyn.d_un.d_val = s->size;
+ dyn.d_un.d_val = htab->elf.srelplt->size + htab->elf.irelplt->size;
break;
}
htab->elf.sgotplt->output_section->vma
+ htab->elf.sgotplt->output_offset,
htab->elf.splt->contents + 24);
- }
+ }
elf_section_data (htab->elf.splt->output_section)
->this_hdr.sh_entsize = 4;
}
}
return TRUE;
}
+\f
+/* Support for core dump NOTE sections. */
static bfd_boolean
elf_s390_grok_prstatus (bfd * abfd, Elf_Internal_Note * note)
switch (note->descsz)
{
- default:
- return FALSE;
+ default:
+ return FALSE;
- case 224: /* S/390 Linux. */
- /* pr_cursig */
- elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
+ case 224: /* S/390 Linux. */
+ /* pr_cursig */
+ elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
- /* pr_pid */
- elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24);
+ /* pr_pid */
+ elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24);
- /* pr_reg */
- offset = 72;
- size = 144;
- break;
+ /* pr_reg */
+ offset = 72;
+ size = 144;
+ break;
}
/* Make a ".reg/999" section. */
size, note->descpos + offset);
}
+static bfd_boolean
+elf_s390_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+ switch (note->descsz)
+ {
+ default:
+ return FALSE;
+
+ case 124: /* sizeof(struct elf_prpsinfo) on s390 */
+ elf_tdata (abfd)->core->pid
+ = bfd_get_32 (abfd, note->descdata + 12);
+ elf_tdata (abfd)->core->program
+ = _bfd_elfcore_strndup (abfd, note->descdata + 28, 16);
+ elf_tdata (abfd)->core->command
+ = _bfd_elfcore_strndup (abfd, note->descdata + 44, 80);
+ break;
+ }
+
+ /* Note that for some reason, a spurious space is tacked
+ onto the end of the args in some (at least one anyway)
+ implementations, so strip it off if it exists. */
+
+ {
+ char *command = elf_tdata (abfd)->core->command;
+ int n = strlen (command);
+
+ if (0 < n && command[n - 1] == ' ')
+ command[n - 1] = '\0';
+ }
+
+ return TRUE;
+}
+
+static char *
+elf_s390_write_core_note (bfd *abfd, char *buf, int *bufsiz,
+ int note_type, ...)
+{
+ va_list ap;
+
+ switch (note_type)
+ {
+ default:
+ return NULL;
+
+ case NT_PRPSINFO:
+ {
+ char data[124] = { 0 };
+ const char *fname, *psargs;
+
+ va_start (ap, note_type);
+ fname = va_arg (ap, const char *);
+ psargs = va_arg (ap, const char *);
+ va_end (ap);
+
+ strncpy (data + 28, fname, 16);
+ strncpy (data + 44, psargs, 80);
+ return elfcore_write_note (abfd, buf, bufsiz, "CORE", note_type,
+ &data, sizeof (data));
+ }
+
+ case NT_PRSTATUS:
+ {
+ char data[224] = { 0 };
+ long pid;
+ int cursig;
+ const void *gregs;
+
+ va_start (ap, note_type);
+ pid = va_arg (ap, long);
+ cursig = va_arg (ap, int);
+ gregs = va_arg (ap, const void *);
+ va_end (ap);
+
+ bfd_put_16 (abfd, cursig, data + 12);
+ bfd_put_32 (abfd, pid, data + 24);
+ memcpy (data + 72, gregs, 144);
+ return elfcore_write_note (abfd, buf, bufsiz, "CORE", note_type,
+ &data, sizeof (data));
+ }
+ }
+ /* NOTREACHED */
+}
+\f
/* Return address for Ith PLT stub in section PLT, for relocation REL
or (bfd_vma) -1 if it should not be included. */
object file when linking. */
static bfd_boolean
-elf32_s390_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+elf32_s390_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
{
+ bfd *obfd = info->output_bfd;
+
if (!is_s390_elf (ibfd) || !is_s390_elf (obfd))
return TRUE;
- if (!elf_s390_merge_obj_attributes (ibfd, obfd))
+ if (!elf_s390_merge_obj_attributes (ibfd, info))
return FALSE;
elf_elfheader (obfd)->e_flags |= elf_elfheader (ibfd)->e_flags;
#define elf_backend_adjust_dynamic_symbol elf_s390_adjust_dynamic_symbol
#define elf_backend_check_relocs elf_s390_check_relocs
#define elf_backend_copy_indirect_symbol elf_s390_copy_indirect_symbol
-#define elf_backend_create_dynamic_sections elf_s390_create_dynamic_sections
+#define elf_backend_create_dynamic_sections _bfd_elf_create_dynamic_sections
#define elf_backend_finish_dynamic_sections elf_s390_finish_dynamic_sections
#define elf_backend_finish_dynamic_symbol elf_s390_finish_dynamic_symbol
#define elf_backend_gc_mark_hook elf_s390_gc_mark_hook
#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_grok_prstatus elf_s390_grok_prstatus
+#define elf_backend_grok_psinfo elf_s390_grok_psinfo
+#define elf_backend_write_core_note elf_s390_write_core_note
#define elf_backend_plt_sym_val elf_s390_plt_sym_val
#define elf_backend_add_symbol_hook elf_s390_add_symbol_hook
#define elf_backend_sort_relocs_p elf_s390_elf_sort_relocs_p