diff options
author | Andreas Krebbel <Andreas.Krebbel@de.ibm.com> | 2012-07-13 15:47:27 +0000 |
---|---|---|
committer | Andreas Krebbel <Andreas.Krebbel@de.ibm.com> | 2012-07-13 15:47:27 +0000 |
commit | 470b557aaf678bdadcd05a04e8fd312ba6d2f098 (patch) | |
tree | e5cd03984d101b6c5a8d4c774cd7c73c4ca14b0a /bfd/elf-s390-common.c | |
parent | 2c3fc38946973ec305b63248abdd170eda059f80 (diff) | |
download | binutils-gdb-470b557aaf678bdadcd05a04e8fd312ba6d2f098.tar.gz |
2012-07-13 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
* elf64-s390.c: Include elf-s390-common.c.
(R_390_IRELATIVE): New reloc.
(elf_s390_reloc_type_lookup): Support R_390_IRELATIVE.
(RELA_ENTRY_SIZE): New macro.
(elf_s390_link_hash_entry): New fields ifunc_resolver_address and
*ifunc_resolver_section.
(struct plt_entry): New struct.
(struct elf_s390_obj_tdata): New field local_plt.
(elf_s390_local_plt): New macro.
(struct elf_s390_link_hash_table): New field irelifunc.
(ELF64): New macro.
(link_hash_newfunc): Initialize new fields.
(elf_s390_check_relocs): Handle IFUNC symbols.
(elf_s390_adjust_dynamic_symbol): Don't do anything for IFUNC
symbols.
(allocate_dynrelocs): Call s390_elf_allocate_ifunc_dyn_relocs for
IFUNC symbols.
(elf_s390_size_dynamic_sections): Handle IFUNC symbols.
(elf_s390_relocate_section): Likewise.
(elf_s390_finish_dynamic_symbol): Likewise.
(elf_s390_finish_dynamic_sections): Handle local IFUNC symbols.
(elf_s390_finish_ifunc_symbol): New function.
(elf_s390_gc_sweep_hook): Handle local plt entries.
(elf_backend_add_symbol_hook): Define.
* elf32-s390.c: See elf64-s390.c changes.
* elf-s390-common.c: New file.
* bfd-in2.h (BFD_RELOC_390_IRELATIVE): New enum field.
* libbfd.h (BFD_RELOC_390_IRELATIVE): New entry for
BFD_RELOC_390_IRELATIVE.
* reloc.c (BFD_RELOC_390_IRELATIVE): Document new relocation.
2012-07-13 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
* elf/s390.h (START_RELOC_NUMBERS): Define R_390_IRELATIVE reloc.
2012-07-13 Andreas Krebbel <Andreas.Krebbel@de.ibm.com>
* emulparams/elf_s390.sh (IREL_IN_PLT): Define.
* emulparams/elf64_s390.sh (IREL_IN_PLT): Define.
Diffstat (limited to 'bfd/elf-s390-common.c')
-rw-r--r-- | bfd/elf-s390-common.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/bfd/elf-s390-common.c b/bfd/elf-s390-common.c new file mode 100644 index 00000000000..691f7511df3 --- /dev/null +++ b/bfd/elf-s390-common.c @@ -0,0 +1,243 @@ +/* IBM S/390-specific support for ELF 32 and 64 bit functions + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + 2011, 2012 Free Software Foundation, Inc. + Contributed by Andreas Krebbel. + + This file is part of BFD, the Binary File Descriptor library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + + +/* Return TRUE if H is an IFUNC symbol. Simply checking for the + symbol type might not be enough since it might get changed to + STT_FUNC for pointer equality reasons. */ +static inline bfd_boolean +s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h) +{ + struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h; + return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0; +} + +/* Create sections needed by STT_GNU_IFUNC symbol. */ + +static bfd_boolean +s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info) +{ + flagword flags; + asection *s; + const struct elf_backend_data *bed = get_elf_backend_data (abfd); + struct elf_link_hash_table *htab = elf_hash_table (info); + + if (htab->iplt != NULL) + return TRUE; + + flags = bed->dynamic_sec_flags; + + if (info->shared) + { + s = bfd_make_section_with_flags (abfd, ".rela.ifunc", + flags | SEC_READONLY); + if (s == NULL + || ! bfd_set_section_alignment (abfd, s, + bed->s->log_file_align)) + return FALSE; + htab->irelifunc = s; + } + + /* Create .iplt, .rel[a].iplt, and .igot.plt. */ + s = bfd_make_section_with_flags (abfd, ".iplt", + flags | SEC_CODE | SEC_READONLY); + 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; + + s = bfd_make_section_with_flags (abfd, ".igot.plt", flags); + if (s == NULL + || !bfd_set_section_alignment (abfd, s, + bed->s->log_file_align)) + return FALSE; + htab->igotplt = s; + + return TRUE; +} + + +/* Allocate space in .plt, .got and associated reloc sections for + dynamic relocs against a STT_GNU_IFUNC symbol definition. */ + +static bfd_boolean +s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info, + struct elf_link_hash_entry *h, + struct elf_dyn_relocs **head) +{ + struct elf_dyn_relocs *p; + struct elf_link_hash_table *htab; + struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h; + + htab = elf_hash_table (info); + eh->ifunc_resolver_address = h->root.u.def.value; + eh->ifunc_resolver_section = h->root.u.def.section; + + /* Support garbage collection against STT_GNU_IFUNC symbols. */ + if (h->plt.refcount <= 0 && h->got.refcount <= 0) + { + /* When building shared library, we need to handle the case + where it is marked with regular reference, but not non-GOT + reference. It may happen if we didn't see STT_GNU_IFUNC + symbol at the time when checking relocations. */ + if (info->shared + && !h->non_got_ref + && h->ref_regular) + for (p = *head; p != NULL; p = p->next) + if (p->count) + { + h->non_got_ref = 1; + goto keep; + } + + h->got = htab->init_got_offset; + h->plt = htab->init_plt_offset; + *head = NULL; + return TRUE; + } + + /* Return and discard space for dynamic relocations against it if + it is never referenced in a non-shared object. */ + if (!h->ref_regular) + { + if (h->plt.refcount > 0 + || h->got.refcount > 0) + abort (); + h->got = htab->init_got_offset; + h->plt = htab->init_plt_offset; + *head = NULL; + return TRUE; + } + +keep: + /* Without checking h->plt.refcount here we allocate a PLT slot. + When setting plt.refcount in check_relocs it might not have been + known that this will be an IFUNC symol. */ + h->plt.offset = htab->iplt->size; + h->needs_plt = 1; + htab->iplt->size += PLT_ENTRY_SIZE; + htab->igotplt->size += GOT_ENTRY_SIZE; + htab->irelplt->size += RELA_ENTRY_SIZE; + htab->irelplt->reloc_count++; + + /* In order to make pointer equality work with IFUNC symbols defined + in a non-PIE executable and referenced in a shared lib, we turn + the symbol into a STT_FUNC symbol and make the symbol value to + point to the IPLT slot. That way the referencing shared lib will + always get the PLT slot address when resolving the respective + R_390_GLOB_DAT/R_390_64 relocs on that symbol. */ + if (info->executable && !info->shared && h->def_regular && h->ref_dynamic) + { + h->root.u.def.section = htab->iplt; + h->root.u.def.value = h->plt.offset; + h->size = PLT_ENTRY_SIZE; + h->type = STT_FUNC; + } + + /* We need dynamic relocation for STT_GNU_IFUNC symbol only when + there is a non-GOT reference in a shared object. */ + if (!info->shared || !h->non_got_ref) + *head = NULL; + + /* Finally, allocate space. */ + p = *head; + if (p != NULL) + { + bfd_size_type count = 0; + do + { + count += p->count; + p = p->next; + } + while (p != NULL); + htab->irelifunc->size += count * RELA_ENTRY_SIZE; + } + + /* Decide whether the got.iplt slot can be used. This has to be + avoided if the values in the GOT slots could differ for pointer + equality reasons. */ + if (h->got.refcount <= 0 + || (info->shared + && (h->dynindx == -1 || h->forced_local)) + || (info->executable && info->shared) + || htab->sgot == NULL) + { + /* Use .got.iplt. */ + h->got.offset = (bfd_vma) -1; + } + else + { + h->got.offset = htab->sgot->size; + htab->sgot->size += GOT_ENTRY_SIZE; + if (info->shared) + htab->srelgot->size += RELA_ENTRY_SIZE; + } + + return TRUE; +} + +static bfd_boolean +elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr) +{ + bfd_size_type size; + + size = symtab_hdr->sh_info; + size *= (sizeof (bfd_signed_vma) /* local got */ + + sizeof (struct plt_entry) /* local plt */ + + sizeof(char)); /* local tls type */ + elf_local_got_refcounts (abfd) = ((bfd_signed_vma *) + bfd_zalloc (abfd, size)); + if (elf_local_got_refcounts (abfd) == NULL) + return FALSE; + elf_s390_local_plt (abfd) + = (struct plt_entry*)(elf_local_got_refcounts (abfd) + + symtab_hdr->sh_info); + elf_s390_local_got_tls_type (abfd) + = (char *) (elf_s390_local_plt (abfd) + symtab_hdr->sh_info); + + return TRUE; +} + +/* Pick ELFOSABI_GNU if IFUNC symbols are used. */ + +static bfd_boolean +elf_s390_add_symbol_hook (bfd *abfd, + struct bfd_link_info *info, + Elf_Internal_Sym *sym, + const char **namep ATTRIBUTE_UNUSED, + flagword *flagsp ATTRIBUTE_UNUSED, + asection **secp ATTRIBUTE_UNUSED, + bfd_vma *valp ATTRIBUTE_UNUSED) +{ + if ((abfd->flags & DYNAMIC) == 0 + && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE; + + return TRUE; +} |