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