Home | History | Annotate | Line # | Download | only in bfd
elf-s390-common.c revision 1.1.1.1
      1  1.1  christos /* IBM S/390-specific support for ELF 32 and 64 bit functions
      2  1.1  christos    Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
      3  1.1  christos    2011, 2012 Free Software Foundation, Inc.
      4  1.1  christos    Contributed by Andreas Krebbel.
      5  1.1  christos 
      6  1.1  christos    This file is part of BFD, the Binary File Descriptor library.
      7  1.1  christos 
      8  1.1  christos    This program is free software; you can redistribute it and/or modify
      9  1.1  christos    it under the terms of the GNU General Public License as published by
     10  1.1  christos    the Free Software Foundation; either version 3 of the License, or
     11  1.1  christos    (at your option) any later version.
     12  1.1  christos 
     13  1.1  christos    This program is distributed in the hope that it will be useful,
     14  1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  1.1  christos    GNU General Public License for more details.
     17  1.1  christos 
     18  1.1  christos    You should have received a copy of the GNU General Public License
     19  1.1  christos    along with this program; if not, write to the Free Software
     20  1.1  christos    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
     21  1.1  christos    02110-1301, USA.  */
     22  1.1  christos 
     23  1.1  christos 
     24  1.1  christos /* Return TRUE if H is an IFUNC symbol.  Simply checking for the
     25  1.1  christos    symbol type might not be enough since it might get changed to
     26  1.1  christos    STT_FUNC for pointer equality reasons.  */
     27  1.1  christos static inline bfd_boolean
     28  1.1  christos s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h)
     29  1.1  christos {
     30  1.1  christos   struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
     31  1.1  christos   return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0;
     32  1.1  christos }
     33  1.1  christos 
     34  1.1  christos /* Create sections needed by STT_GNU_IFUNC symbol.  */
     35  1.1  christos 
     36  1.1  christos static bfd_boolean
     37  1.1  christos s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
     38  1.1  christos {
     39  1.1  christos   flagword flags;
     40  1.1  christos   asection *s;
     41  1.1  christos   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
     42  1.1  christos   struct elf_link_hash_table *htab = elf_hash_table (info);
     43  1.1  christos 
     44  1.1  christos   if (htab->iplt != NULL)
     45  1.1  christos     return TRUE;
     46  1.1  christos 
     47  1.1  christos   flags = bed->dynamic_sec_flags;
     48  1.1  christos 
     49  1.1  christos   if (info->shared)
     50  1.1  christos     {
     51  1.1  christos       s = bfd_make_section_with_flags (abfd, ".rela.ifunc",
     52  1.1  christos 				       flags | SEC_READONLY);
     53  1.1  christos       if (s == NULL
     54  1.1  christos 	  || ! bfd_set_section_alignment (abfd, s,
     55  1.1  christos 					  bed->s->log_file_align))
     56  1.1  christos 	return FALSE;
     57  1.1  christos       htab->irelifunc = s;
     58  1.1  christos     }
     59  1.1  christos 
     60  1.1  christos   /* Create .iplt, .rel[a].iplt, and .igot.plt.  */
     61  1.1  christos   s = bfd_make_section_with_flags (abfd, ".iplt",
     62  1.1  christos 				   flags | SEC_CODE | SEC_READONLY);
     63  1.1  christos   if (s == NULL
     64  1.1  christos       || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
     65  1.1  christos     return FALSE;
     66  1.1  christos   htab->iplt = s;
     67  1.1  christos 
     68  1.1  christos   s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY);
     69  1.1  christos   if (s == NULL
     70  1.1  christos       || ! bfd_set_section_alignment (abfd, s,
     71  1.1  christos 				      bed->s->log_file_align))
     72  1.1  christos     return FALSE;
     73  1.1  christos   htab->irelplt = s;
     74  1.1  christos 
     75  1.1  christos   s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
     76  1.1  christos   if (s == NULL
     77  1.1  christos       || !bfd_set_section_alignment (abfd, s,
     78  1.1  christos 				     bed->s->log_file_align))
     79  1.1  christos     return FALSE;
     80  1.1  christos   htab->igotplt = s;
     81  1.1  christos 
     82  1.1  christos   return TRUE;
     83  1.1  christos }
     84  1.1  christos 
     85  1.1  christos 
     86  1.1  christos /* Allocate space in .plt, .got and associated reloc sections for
     87  1.1  christos    dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
     88  1.1  christos 
     89  1.1  christos static bfd_boolean
     90  1.1  christos s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
     91  1.1  christos 				    struct elf_link_hash_entry *h,
     92  1.1  christos 				    struct elf_dyn_relocs **head)
     93  1.1  christos {
     94  1.1  christos   struct elf_dyn_relocs *p;
     95  1.1  christos   struct elf_link_hash_table *htab;
     96  1.1  christos   struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
     97  1.1  christos 
     98  1.1  christos   htab = elf_hash_table (info);
     99  1.1  christos   eh->ifunc_resolver_address = h->root.u.def.value;
    100  1.1  christos   eh->ifunc_resolver_section = h->root.u.def.section;
    101  1.1  christos 
    102  1.1  christos   /* Support garbage collection against STT_GNU_IFUNC symbols.  */
    103  1.1  christos   if (h->plt.refcount <= 0 && h->got.refcount <= 0)
    104  1.1  christos     {
    105  1.1  christos       /* When building shared library, we need to handle the case
    106  1.1  christos          where it is marked with regular reference, but not non-GOT
    107  1.1  christos 	 reference.  It may happen if we didn't see STT_GNU_IFUNC
    108  1.1  christos 	 symbol at the time when checking relocations.  */
    109  1.1  christos       if (info->shared
    110  1.1  christos 	  && !h->non_got_ref
    111  1.1  christos 	  && h->ref_regular)
    112  1.1  christos 	for (p = *head; p != NULL; p = p->next)
    113  1.1  christos 	  if (p->count)
    114  1.1  christos 	    {
    115  1.1  christos 	      h->non_got_ref = 1;
    116  1.1  christos 	      goto keep;
    117  1.1  christos 	    }
    118  1.1  christos 
    119  1.1  christos       h->got = htab->init_got_offset;
    120  1.1  christos       h->plt = htab->init_plt_offset;
    121  1.1  christos       *head = NULL;
    122  1.1  christos       return TRUE;
    123  1.1  christos     }
    124  1.1  christos 
    125  1.1  christos   /* Return and discard space for dynamic relocations against it if
    126  1.1  christos      it is never referenced in a non-shared object.  */
    127  1.1  christos   if (!h->ref_regular)
    128  1.1  christos     {
    129  1.1  christos       if (h->plt.refcount > 0
    130  1.1  christos 	  || h->got.refcount > 0)
    131  1.1  christos 	abort ();
    132  1.1  christos       h->got = htab->init_got_offset;
    133  1.1  christos       h->plt = htab->init_plt_offset;
    134  1.1  christos       *head = NULL;
    135  1.1  christos       return TRUE;
    136  1.1  christos     }
    137  1.1  christos 
    138  1.1  christos keep:
    139  1.1  christos   /* Without checking h->plt.refcount here we allocate a PLT slot.
    140  1.1  christos      When setting plt.refcount in check_relocs it might not have been
    141  1.1  christos      known that this will be an IFUNC symol.  */
    142  1.1  christos   h->plt.offset = htab->iplt->size;
    143  1.1  christos   h->needs_plt = 1;
    144  1.1  christos   htab->iplt->size += PLT_ENTRY_SIZE;
    145  1.1  christos   htab->igotplt->size += GOT_ENTRY_SIZE;
    146  1.1  christos   htab->irelplt->size += RELA_ENTRY_SIZE;
    147  1.1  christos   htab->irelplt->reloc_count++;
    148  1.1  christos 
    149  1.1  christos   /* In order to make pointer equality work with IFUNC symbols defined
    150  1.1  christos      in a non-PIE executable and referenced in a shared lib, we turn
    151  1.1  christos      the symbol into a STT_FUNC symbol and make the symbol value to
    152  1.1  christos      point to the IPLT slot.  That way the referencing shared lib will
    153  1.1  christos      always get the PLT slot address when resolving the respective
    154  1.1  christos      R_390_GLOB_DAT/R_390_64 relocs on that symbol.  */
    155  1.1  christos   if (info->executable && !info->shared && h->def_regular && h->ref_dynamic)
    156  1.1  christos     {
    157  1.1  christos       h->root.u.def.section = htab->iplt;
    158  1.1  christos       h->root.u.def.value = h->plt.offset;
    159  1.1  christos       h->size = PLT_ENTRY_SIZE;
    160  1.1  christos       h->type = STT_FUNC;
    161  1.1  christos     }
    162  1.1  christos 
    163  1.1  christos   /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
    164  1.1  christos      there is a non-GOT reference in a shared object.  */
    165  1.1  christos   if (!info->shared || !h->non_got_ref)
    166  1.1  christos     *head = NULL;
    167  1.1  christos 
    168  1.1  christos   /* Finally, allocate space.  */
    169  1.1  christos   p = *head;
    170  1.1  christos   if (p != NULL)
    171  1.1  christos     {
    172  1.1  christos       bfd_size_type count = 0;
    173  1.1  christos       do
    174  1.1  christos 	{
    175  1.1  christos 	  count += p->count;
    176  1.1  christos 	  p = p->next;
    177  1.1  christos 	}
    178  1.1  christos       while (p != NULL);
    179  1.1  christos       htab->irelifunc->size += count * RELA_ENTRY_SIZE;
    180  1.1  christos     }
    181  1.1  christos 
    182  1.1  christos   /* Decide whether the got.iplt slot can be used.  This has to be
    183  1.1  christos      avoided if the values in the GOT slots could differ for pointer
    184  1.1  christos      equality reasons.  */
    185  1.1  christos   if (h->got.refcount <= 0
    186  1.1  christos       || (info->shared
    187  1.1  christos 	  && (h->dynindx == -1 || h->forced_local))
    188  1.1  christos       || (info->executable && info->shared)
    189  1.1  christos       || htab->sgot == NULL)
    190  1.1  christos     {
    191  1.1  christos       /* Use .got.iplt.  */
    192  1.1  christos       h->got.offset = (bfd_vma) -1;
    193  1.1  christos     }
    194  1.1  christos   else
    195  1.1  christos     {
    196  1.1  christos       h->got.offset = htab->sgot->size;
    197  1.1  christos       htab->sgot->size += GOT_ENTRY_SIZE;
    198  1.1  christos       if (info->shared)
    199  1.1  christos 	htab->srelgot->size += RELA_ENTRY_SIZE;
    200  1.1  christos     }
    201  1.1  christos 
    202  1.1  christos   return TRUE;
    203  1.1  christos }
    204  1.1  christos 
    205  1.1  christos static bfd_boolean
    206  1.1  christos elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr)
    207  1.1  christos {
    208  1.1  christos   bfd_size_type size;
    209  1.1  christos 
    210  1.1  christos   size = symtab_hdr->sh_info;
    211  1.1  christos   size *= (sizeof (bfd_signed_vma)       /* local got */
    212  1.1  christos 	   + sizeof (struct plt_entry)   /* local plt */
    213  1.1  christos 	   + sizeof(char));              /* local tls type */
    214  1.1  christos   elf_local_got_refcounts (abfd) = ((bfd_signed_vma *)
    215  1.1  christos 				    bfd_zalloc (abfd, size));
    216  1.1  christos   if (elf_local_got_refcounts (abfd) == NULL)
    217  1.1  christos     return FALSE;
    218  1.1  christos   elf_s390_local_plt (abfd)
    219  1.1  christos     = (struct plt_entry*)(elf_local_got_refcounts (abfd)
    220  1.1  christos 			  + symtab_hdr->sh_info);
    221  1.1  christos   elf_s390_local_got_tls_type (abfd)
    222  1.1  christos     = (char *) (elf_s390_local_plt (abfd) + symtab_hdr->sh_info);
    223  1.1  christos 
    224  1.1  christos   return TRUE;
    225  1.1  christos }
    226  1.1  christos 
    227  1.1  christos /* Pick ELFOSABI_GNU if IFUNC symbols are used.  */
    228  1.1  christos 
    229  1.1  christos static bfd_boolean
    230  1.1  christos elf_s390_add_symbol_hook (bfd *abfd,
    231  1.1  christos 			  struct bfd_link_info *info,
    232  1.1  christos 			  Elf_Internal_Sym *sym,
    233  1.1  christos 			  const char **namep ATTRIBUTE_UNUSED,
    234  1.1  christos 			  flagword *flagsp ATTRIBUTE_UNUSED,
    235  1.1  christos 			  asection **secp ATTRIBUTE_UNUSED,
    236  1.1  christos 			  bfd_vma *valp ATTRIBUTE_UNUSED)
    237  1.1  christos {
    238  1.1  christos   if ((abfd->flags & DYNAMIC) == 0
    239  1.1  christos       && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
    240  1.1  christos     elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
    241  1.1  christos 
    242  1.1  christos   return TRUE;
    243  1.1  christos }
    244