Home | History | Annotate | Line # | Download | only in bfd
      1       1.1  christos /* IBM S/390-specific support for ELF 32 and 64 bit functions
      2  1.1.1.10  christos    Copyright (C) 2000-2026 Free Software Foundation, Inc.
      3       1.1  christos    Contributed by Andreas Krebbel.
      4       1.1  christos 
      5       1.1  christos    This file is part of BFD, the Binary File Descriptor library.
      6       1.1  christos 
      7       1.1  christos    This program is free software; you can redistribute it and/or modify
      8       1.1  christos    it under the terms of the GNU General Public License as published by
      9       1.1  christos    the Free Software Foundation; either version 3 of the License, or
     10       1.1  christos    (at your option) any later version.
     11       1.1  christos 
     12       1.1  christos    This program is distributed in the hope that it will be useful,
     13       1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14       1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15       1.1  christos    GNU General Public License for more details.
     16       1.1  christos 
     17       1.1  christos    You should have received a copy of the GNU General Public License
     18       1.1  christos    along with this program; if not, write to the Free Software
     19       1.1  christos    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
     20       1.1  christos    02110-1301, USA.  */
     21       1.1  christos 
     22       1.1  christos 
     23       1.1  christos /* Return TRUE if H is an IFUNC symbol.  Simply checking for the
     24       1.1  christos    symbol type might not be enough since it might get changed to
     25       1.1  christos    STT_FUNC for pointer equality reasons.  */
     26   1.1.1.7  christos static inline bool
     27       1.1  christos s390_is_ifunc_symbol_p (struct elf_link_hash_entry *h)
     28       1.1  christos {
     29       1.1  christos   struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
     30       1.1  christos   return h->type == STT_GNU_IFUNC || eh->ifunc_resolver_address != 0;
     31       1.1  christos }
     32       1.1  christos 
     33   1.1.1.6  christos /* Return true if .got.plt is supposed to be emitted after .got.  */
     34   1.1.1.6  christos 
     35   1.1.1.7  christos static inline bool
     36   1.1.1.6  christos s390_gotplt_after_got_p (struct bfd_link_info *info)
     37   1.1.1.6  christos {
     38   1.1.1.6  christos   struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
     39   1.1.1.6  christos 
     40   1.1.1.6  christos   if (!htab->elf.sgot || !htab->elf.sgotplt)
     41   1.1.1.7  christos     return true;
     42   1.1.1.6  christos 
     43   1.1.1.6  christos   if (htab->elf.sgot->output_section == htab->elf.sgotplt->output_section)
     44   1.1.1.6  christos     {
     45   1.1.1.6  christos       if (htab->elf.sgot->output_offset < htab->elf.sgotplt->output_offset)
     46   1.1.1.7  christos 	return true;
     47   1.1.1.6  christos     }
     48   1.1.1.6  christos   else
     49   1.1.1.6  christos     {
     50   1.1.1.6  christos       if (htab->elf.sgot->output_section->vma
     51   1.1.1.6  christos 	  <= htab->elf.sgotplt->output_section->vma)
     52   1.1.1.7  christos 	return true;
     53   1.1.1.6  christos     }
     54   1.1.1.7  christos   return false;
     55   1.1.1.6  christos }
     56   1.1.1.6  christos 
     57   1.1.1.6  christos /* Return the value of the _GLOBAL_OFFSET_TABLE_ symbol.  */
     58   1.1.1.6  christos 
     59   1.1.1.6  christos static inline bfd_vma
     60   1.1.1.6  christos s390_got_pointer (struct bfd_link_info *info)
     61   1.1.1.6  christos {
     62   1.1.1.6  christos   struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
     63   1.1.1.6  christos   bfd_vma got_pointer;
     64   1.1.1.6  christos 
     65   1.1.1.6  christos   BFD_ASSERT (htab && htab->elf.hgot);
     66   1.1.1.6  christos 
     67   1.1.1.6  christos   got_pointer = (htab->elf.hgot->root.u.def.section->output_section->vma
     68   1.1.1.6  christos 		 + htab->elf.hgot->root.u.def.section->output_offset);
     69   1.1.1.6  christos   /* Our ABI requires the GOT pointer to point at the very beginning
     70   1.1.1.6  christos      of the global offset table.  */
     71   1.1.1.6  christos   BFD_ASSERT (got_pointer
     72   1.1.1.6  christos 	      <= (htab->elf.sgot->output_section->vma
     73   1.1.1.6  christos 		  + htab->elf.sgot->output_offset));
     74   1.1.1.6  christos   BFD_ASSERT (got_pointer
     75   1.1.1.6  christos 	      <= (htab->elf.sgotplt->output_section->vma
     76   1.1.1.6  christos 		  + htab->elf.sgotplt->output_offset));
     77   1.1.1.6  christos 
     78   1.1.1.6  christos   return got_pointer;
     79   1.1.1.6  christos }
     80   1.1.1.6  christos 
     81   1.1.1.6  christos 
     82   1.1.1.6  christos /* Return the offset of the .got versus _GLOBAL_OFFSET_TABLE_.  */
     83   1.1.1.6  christos 
     84   1.1.1.6  christos static inline bfd_vma
     85   1.1.1.6  christos s390_got_offset (struct bfd_link_info *info)
     86   1.1.1.6  christos {
     87   1.1.1.6  christos   struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
     88   1.1.1.6  christos 
     89   1.1.1.6  christos   /* The absolute address of the .got in the target image.  */
     90   1.1.1.6  christos   bfd_vma got_address = (htab->elf.sgot->output_section->vma
     91   1.1.1.6  christos 			 + htab->elf.sgot->output_offset);
     92   1.1.1.6  christos 
     93   1.1.1.6  christos   /* GOT offset must not be negative.  */
     94   1.1.1.6  christos   BFD_ASSERT (s390_got_pointer (info) <= got_address);
     95   1.1.1.6  christos   return got_address - s390_got_pointer (info);
     96   1.1.1.6  christos }
     97   1.1.1.6  christos 
     98   1.1.1.6  christos /* Return the offset of the .got.plt versus _GLOBAL_OFFSET_TABLE_.  */
     99   1.1.1.6  christos 
    100   1.1.1.6  christos static inline bfd_vma
    101   1.1.1.6  christos s390_gotplt_offset (struct bfd_link_info *info)
    102   1.1.1.6  christos {
    103   1.1.1.6  christos   struct elf_s390_link_hash_table *htab = elf_s390_hash_table (info);
    104   1.1.1.6  christos 
    105   1.1.1.6  christos   /* The absolute address of the .got.plt in the target image.  */
    106   1.1.1.6  christos   bfd_vma gotplt_address = (htab->elf.sgotplt->output_section->vma
    107   1.1.1.6  christos 			    + htab->elf.sgotplt->output_offset);
    108   1.1.1.6  christos 
    109   1.1.1.6  christos   /* GOT offset must not be negative.  */
    110   1.1.1.6  christos   BFD_ASSERT (s390_got_pointer (info) <= gotplt_address);
    111   1.1.1.6  christos   return gotplt_address - s390_got_pointer (info);
    112   1.1.1.6  christos }
    113   1.1.1.6  christos 
    114       1.1  christos /* Create sections needed by STT_GNU_IFUNC symbol.  */
    115       1.1  christos 
    116   1.1.1.7  christos static bool
    117       1.1  christos s390_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
    118       1.1  christos {
    119       1.1  christos   flagword flags;
    120       1.1  christos   asection *s;
    121  1.1.1.10  christos   elf_backend_data *bed = get_elf_backend_data (abfd);
    122       1.1  christos   struct elf_link_hash_table *htab = elf_hash_table (info);
    123       1.1  christos 
    124       1.1  christos   if (htab->iplt != NULL)
    125   1.1.1.7  christos     return true;
    126       1.1  christos 
    127       1.1  christos   flags = bed->dynamic_sec_flags;
    128       1.1  christos 
    129   1.1.1.2  christos   if (bfd_link_pic (info))
    130       1.1  christos     {
    131       1.1  christos       s = bfd_make_section_with_flags (abfd, ".rela.ifunc",
    132       1.1  christos 				       flags | SEC_READONLY);
    133       1.1  christos       if (s == NULL
    134   1.1.1.6  christos 	  || !bfd_set_section_alignment (s, bed->s->log_file_align))
    135   1.1.1.7  christos 	return false;
    136       1.1  christos       htab->irelifunc = s;
    137       1.1  christos     }
    138       1.1  christos 
    139       1.1  christos   /* Create .iplt, .rel[a].iplt, and .igot.plt.  */
    140       1.1  christos   s = bfd_make_section_with_flags (abfd, ".iplt",
    141       1.1  christos 				   flags | SEC_CODE | SEC_READONLY);
    142       1.1  christos   if (s == NULL
    143   1.1.1.6  christos       || !bfd_set_section_alignment (s, bed->plt_alignment))
    144   1.1.1.7  christos     return false;
    145       1.1  christos   htab->iplt = s;
    146       1.1  christos 
    147       1.1  christos   s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags | SEC_READONLY);
    148       1.1  christos   if (s == NULL
    149   1.1.1.6  christos       || !bfd_set_section_alignment (s, bed->s->log_file_align))
    150   1.1.1.7  christos     return false;
    151       1.1  christos   htab->irelplt = s;
    152       1.1  christos 
    153       1.1  christos   s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
    154       1.1  christos   if (s == NULL
    155   1.1.1.6  christos       || !bfd_set_section_alignment (s, bed->s->log_file_align))
    156   1.1.1.7  christos     return false;
    157       1.1  christos   htab->igotplt = s;
    158       1.1  christos 
    159   1.1.1.7  christos   return true;
    160       1.1  christos }
    161       1.1  christos 
    162       1.1  christos 
    163       1.1  christos /* Allocate space in .plt, .got and associated reloc sections for
    164       1.1  christos    dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
    165       1.1  christos 
    166   1.1.1.7  christos static bool
    167       1.1  christos s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
    168   1.1.1.2  christos 				    struct elf_link_hash_entry *h)
    169       1.1  christos {
    170       1.1  christos   struct elf_dyn_relocs *p;
    171       1.1  christos   struct elf_link_hash_table *htab;
    172       1.1  christos   struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
    173   1.1.1.7  christos   struct elf_dyn_relocs **head = &h->dyn_relocs;
    174       1.1  christos 
    175       1.1  christos   htab = elf_hash_table (info);
    176       1.1  christos   eh->ifunc_resolver_address = h->root.u.def.value;
    177       1.1  christos   eh->ifunc_resolver_section = h->root.u.def.section;
    178       1.1  christos 
    179       1.1  christos   /* Support garbage collection against STT_GNU_IFUNC symbols.  */
    180       1.1  christos   if (h->plt.refcount <= 0 && h->got.refcount <= 0)
    181       1.1  christos     {
    182       1.1  christos       /* When building shared library, we need to handle the case
    183   1.1.1.4  christos 	 where it is marked with regular reference, but not non-GOT
    184       1.1  christos 	 reference.  It may happen if we didn't see STT_GNU_IFUNC
    185       1.1  christos 	 symbol at the time when checking relocations.  */
    186   1.1.1.2  christos       if (bfd_link_pic (info)
    187       1.1  christos 	  && !h->non_got_ref
    188       1.1  christos 	  && h->ref_regular)
    189       1.1  christos 	for (p = *head; p != NULL; p = p->next)
    190       1.1  christos 	  if (p->count)
    191       1.1  christos 	    {
    192       1.1  christos 	      h->non_got_ref = 1;
    193       1.1  christos 	      goto keep;
    194       1.1  christos 	    }
    195       1.1  christos 
    196       1.1  christos       h->got = htab->init_got_offset;
    197       1.1  christos       h->plt = htab->init_plt_offset;
    198       1.1  christos       *head = NULL;
    199   1.1.1.7  christos       return true;
    200       1.1  christos     }
    201       1.1  christos 
    202       1.1  christos   /* Return and discard space for dynamic relocations against it if
    203       1.1  christos      it is never referenced in a non-shared object.  */
    204       1.1  christos   if (!h->ref_regular)
    205       1.1  christos     {
    206       1.1  christos       if (h->plt.refcount > 0
    207       1.1  christos 	  || h->got.refcount > 0)
    208       1.1  christos 	abort ();
    209       1.1  christos       h->got = htab->init_got_offset;
    210       1.1  christos       h->plt = htab->init_plt_offset;
    211       1.1  christos       *head = NULL;
    212   1.1.1.7  christos       return true;
    213       1.1  christos     }
    214       1.1  christos 
    215   1.1.1.7  christos  keep:
    216       1.1  christos   /* Without checking h->plt.refcount here we allocate a PLT slot.
    217       1.1  christos      When setting plt.refcount in check_relocs it might not have been
    218       1.1  christos      known that this will be an IFUNC symol.  */
    219       1.1  christos   h->plt.offset = htab->iplt->size;
    220       1.1  christos   h->needs_plt = 1;
    221       1.1  christos   htab->iplt->size += PLT_ENTRY_SIZE;
    222       1.1  christos   htab->igotplt->size += GOT_ENTRY_SIZE;
    223       1.1  christos   htab->irelplt->size += RELA_ENTRY_SIZE;
    224       1.1  christos   htab->irelplt->reloc_count++;
    225       1.1  christos 
    226       1.1  christos   /* In order to make pointer equality work with IFUNC symbols defined
    227       1.1  christos      in a non-PIE executable and referenced in a shared lib, we turn
    228       1.1  christos      the symbol into a STT_FUNC symbol and make the symbol value to
    229       1.1  christos      point to the IPLT slot.  That way the referencing shared lib will
    230       1.1  christos      always get the PLT slot address when resolving the respective
    231       1.1  christos      R_390_GLOB_DAT/R_390_64 relocs on that symbol.  */
    232   1.1.1.2  christos   if (bfd_link_pde (info)
    233   1.1.1.2  christos       && h->def_regular
    234   1.1.1.2  christos       && h->ref_dynamic)
    235       1.1  christos     {
    236       1.1  christos       h->root.u.def.section = htab->iplt;
    237       1.1  christos       h->root.u.def.value = h->plt.offset;
    238       1.1  christos       h->size = PLT_ENTRY_SIZE;
    239       1.1  christos       h->type = STT_FUNC;
    240       1.1  christos     }
    241       1.1  christos 
    242   1.1.1.4  christos   if (!bfd_link_pic (info))
    243       1.1  christos     *head = NULL;
    244       1.1  christos 
    245       1.1  christos   /* Finally, allocate space.  */
    246       1.1  christos   p = *head;
    247       1.1  christos   if (p != NULL)
    248       1.1  christos     {
    249       1.1  christos       bfd_size_type count = 0;
    250       1.1  christos       do
    251       1.1  christos 	{
    252       1.1  christos 	  count += p->count;
    253       1.1  christos 	  p = p->next;
    254       1.1  christos 	}
    255       1.1  christos       while (p != NULL);
    256       1.1  christos       htab->irelifunc->size += count * RELA_ENTRY_SIZE;
    257       1.1  christos     }
    258       1.1  christos 
    259       1.1  christos   /* Decide whether the got.iplt slot can be used.  This has to be
    260       1.1  christos      avoided if the values in the GOT slots could differ for pointer
    261       1.1  christos      equality reasons.  */
    262       1.1  christos   if (h->got.refcount <= 0
    263   1.1.1.2  christos       || (bfd_link_pic (info)
    264       1.1  christos 	  && (h->dynindx == -1 || h->forced_local))
    265   1.1.1.2  christos       || bfd_link_pie (info)
    266       1.1  christos       || htab->sgot == NULL)
    267       1.1  christos     {
    268       1.1  christos       /* Use .got.iplt.  */
    269       1.1  christos       h->got.offset = (bfd_vma) -1;
    270       1.1  christos     }
    271       1.1  christos   else
    272       1.1  christos     {
    273       1.1  christos       h->got.offset = htab->sgot->size;
    274       1.1  christos       htab->sgot->size += GOT_ENTRY_SIZE;
    275   1.1.1.2  christos       if (bfd_link_pic (info))
    276       1.1  christos 	htab->srelgot->size += RELA_ENTRY_SIZE;
    277       1.1  christos     }
    278       1.1  christos 
    279   1.1.1.7  christos   return true;
    280       1.1  christos }
    281       1.1  christos 
    282   1.1.1.7  christos static bool
    283       1.1  christos elf_s390_allocate_local_syminfo (bfd *abfd, Elf_Internal_Shdr *symtab_hdr)
    284       1.1  christos {
    285       1.1  christos   bfd_size_type size;
    286       1.1  christos 
    287       1.1  christos   size = symtab_hdr->sh_info;
    288   1.1.1.4  christos   size *= (sizeof (bfd_signed_vma)	 /* local got */
    289   1.1.1.4  christos 	   + sizeof (struct plt_entry)	 /* local plt */
    290   1.1.1.4  christos 	   + sizeof(char));		 /* local tls type */
    291       1.1  christos   elf_local_got_refcounts (abfd) = ((bfd_signed_vma *)
    292       1.1  christos 				    bfd_zalloc (abfd, size));
    293       1.1  christos   if (elf_local_got_refcounts (abfd) == NULL)
    294   1.1.1.7  christos     return false;
    295       1.1  christos   elf_s390_local_plt (abfd)
    296       1.1  christos     = (struct plt_entry*)(elf_local_got_refcounts (abfd)
    297       1.1  christos 			  + symtab_hdr->sh_info);
    298       1.1  christos   elf_s390_local_got_tls_type (abfd)
    299       1.1  christos     = (char *) (elf_s390_local_plt (abfd) + symtab_hdr->sh_info);
    300       1.1  christos 
    301   1.1.1.7  christos   return true;
    302       1.1  christos }
    303       1.1  christos 
    304   1.1.1.2  christos /* Whether to sort relocs output by ld -r or ld --emit-relocs, by
    305   1.1.1.2  christos    r_offset.  Don't do so for code sections.  We want to keep ordering
    306   1.1.1.2  christos    of GDCALL / PLT32DBL for TLS optimizations as is.  On the other
    307   1.1.1.2  christos    hand, elf-eh-frame.c processing requires .eh_frame relocs to be
    308   1.1.1.2  christos    sorted.  */
    309   1.1.1.2  christos 
    310   1.1.1.7  christos static bool
    311   1.1.1.2  christos elf_s390_elf_sort_relocs_p (asection *sec)
    312   1.1.1.2  christos {
    313   1.1.1.2  christos   return (sec->flags & SEC_CODE) == 0;
    314   1.1.1.2  christos }
    315   1.1.1.2  christos 
    316   1.1.1.2  christos /* Merge object attributes from IBFD into OBFD.  Raise an error if
    317   1.1.1.2  christos    there are conflicting attributes.  */
    318   1.1.1.7  christos static bool
    319   1.1.1.4  christos elf_s390_merge_obj_attributes (bfd *ibfd, struct bfd_link_info *info)
    320   1.1.1.2  christos {
    321   1.1.1.4  christos   bfd *obfd = info->output_bfd;
    322   1.1.1.2  christos   obj_attribute *in_attr, *in_attrs;
    323   1.1.1.2  christos   obj_attribute *out_attr, *out_attrs;
    324   1.1.1.2  christos 
    325   1.1.1.2  christos   if (!elf_known_obj_attributes_proc (obfd)[0].i)
    326   1.1.1.2  christos     {
    327   1.1.1.2  christos       /* This is the first object.  Copy the attributes.  */
    328   1.1.1.2  christos       _bfd_elf_copy_obj_attributes (ibfd, obfd);
    329   1.1.1.2  christos 
    330   1.1.1.2  christos       /* Use the Tag_null value to indicate the attributes have been
    331   1.1.1.2  christos 	 initialized.  */
    332   1.1.1.2  christos       elf_known_obj_attributes_proc (obfd)[0].i = 1;
    333   1.1.1.2  christos 
    334   1.1.1.7  christos       return true;
    335   1.1.1.2  christos     }
    336   1.1.1.2  christos 
    337   1.1.1.2  christos   in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
    338   1.1.1.2  christos   out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
    339   1.1.1.2  christos 
    340   1.1.1.2  christos   /* Check for conflicting Tag_GNU_S390_ABI_Vector attributes and
    341   1.1.1.2  christos      merge non-conflicting ones.  */
    342   1.1.1.2  christos   in_attr = &in_attrs[Tag_GNU_S390_ABI_Vector];
    343   1.1.1.2  christos   out_attr = &out_attrs[Tag_GNU_S390_ABI_Vector];
    344   1.1.1.2  christos 
    345   1.1.1.2  christos   if (in_attr->i > 2)
    346   1.1.1.2  christos     _bfd_error_handler
    347   1.1.1.4  christos       /* xgettext:c-format */
    348   1.1.1.5  christos       (_("warning: %pB uses unknown vector ABI %d"), ibfd,
    349   1.1.1.2  christos        in_attr->i);
    350   1.1.1.2  christos   else if (out_attr->i > 2)
    351   1.1.1.2  christos     _bfd_error_handler
    352   1.1.1.4  christos       /* xgettext:c-format */
    353   1.1.1.5  christos       (_("warning: %pB uses unknown vector ABI %d"), obfd,
    354   1.1.1.2  christos        out_attr->i);
    355   1.1.1.2  christos   else if (in_attr->i != out_attr->i)
    356   1.1.1.2  christos     {
    357   1.1.1.2  christos       out_attr->type = ATTR_TYPE_FLAG_INT_VAL;
    358   1.1.1.2  christos 
    359   1.1.1.2  christos       if (in_attr->i && out_attr->i)
    360   1.1.1.2  christos 	{
    361   1.1.1.2  christos 	  const char abi_str[3][9] = { "none", "software", "hardware" };
    362   1.1.1.2  christos 
    363   1.1.1.2  christos 	  _bfd_error_handler
    364   1.1.1.4  christos 	    /* xgettext:c-format */
    365   1.1.1.5  christos 	    (_("warning: %pB uses vector %s ABI, %pB uses %s ABI"),
    366   1.1.1.4  christos 	     ibfd, abi_str[in_attr->i], obfd, abi_str[out_attr->i]);
    367   1.1.1.2  christos 	}
    368   1.1.1.2  christos       if (in_attr->i > out_attr->i)
    369   1.1.1.2  christos 	out_attr->i = in_attr->i;
    370   1.1.1.2  christos     }
    371   1.1.1.2  christos 
    372   1.1.1.2  christos   /* Merge Tag_compatibility attributes and any common GNU ones.  */
    373   1.1.1.4  christos   _bfd_elf_merge_object_attributes (ibfd, info);
    374       1.1  christos 
    375   1.1.1.7  christos   return true;
    376       1.1  christos }
    377