(root)/
binutils-2.41/
bfd/
elf-ifunc.c
       1  /* ELF STT_GNU_IFUNC support.
       2     Copyright (C) 2009-2023 Free Software Foundation, Inc.
       3  
       4     This file is part of BFD, the Binary File Descriptor library.
       5  
       6     This program is free software; you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation; either version 3 of the License, or
       9     (at your option) any later version.
      10  
      11     This program is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with this program; if not, write to the Free Software
      18     Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
      19     MA 02110-1301, USA.  */
      20  
      21  #include "sysdep.h"
      22  #include "bfd.h"
      23  #include "bfdlink.h"
      24  #include "libbfd.h"
      25  #define ARCH_SIZE 0
      26  #include "elf-bfd.h"
      27  #include "safe-ctype.h"
      28  #include "libiberty.h"
      29  #include "objalloc.h"
      30  
      31  /* Create sections needed by STT_GNU_IFUNC symbol.  */
      32  
      33  bool
      34  _bfd_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
      35  {
      36    flagword flags, pltflags;
      37    asection *s;
      38    const struct elf_backend_data *bed = get_elf_backend_data (abfd);
      39    struct elf_link_hash_table *htab = elf_hash_table (info);
      40  
      41    if (htab->irelifunc != NULL || htab->iplt != NULL)
      42      return true;
      43  
      44    flags = bed->dynamic_sec_flags;
      45    pltflags = flags;
      46    if (bed->plt_not_loaded)
      47      /* We do not clear SEC_ALLOC here because we still want the OS to
      48         allocate space for the section; it's just that there's nothing
      49         to read in from the object file.  */
      50      pltflags &= ~ (SEC_CODE | SEC_LOAD | SEC_HAS_CONTENTS);
      51    else
      52      pltflags |= SEC_ALLOC | SEC_CODE | SEC_LOAD;
      53    if (bed->plt_readonly)
      54      pltflags |= SEC_READONLY;
      55  
      56    if (bfd_link_pic (info))
      57      {
      58        /* We need to create .rel[a].ifunc for PIC objects.  */
      59        const char *rel_sec = (bed->rela_plts_and_copies_p
      60  			     ? ".rela.ifunc" : ".rel.ifunc");
      61  
      62        s = bfd_make_section_with_flags (abfd, rel_sec,
      63  				       flags | SEC_READONLY);
      64        if (s == NULL
      65  	  || !bfd_set_section_alignment (s, bed->s->log_file_align))
      66  	return false;
      67        htab->irelifunc = s;
      68      }
      69    else
      70      {
      71        /* We need to create .iplt, .rel[a].iplt, .igot and .igot.plt
      72  	 for static executables.   */
      73        s = bfd_make_section_with_flags (abfd, ".iplt", pltflags);
      74        if (s == NULL
      75  	  || !bfd_set_section_alignment (s, bed->plt_alignment))
      76  	return false;
      77        htab->iplt = s;
      78  
      79        s = bfd_make_section_with_flags (abfd,
      80  				       (bed->rela_plts_and_copies_p
      81  					? ".rela.iplt" : ".rel.iplt"),
      82  				       flags | SEC_READONLY);
      83        if (s == NULL
      84  	  || !bfd_set_section_alignment (s, bed->s->log_file_align))
      85  	return false;
      86        htab->irelplt = s;
      87  
      88        /* We don't need the .igot section if we have the .igot.plt
      89  	 section.  */
      90        if (bed->want_got_plt)
      91  	s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
      92        else
      93  	s = bfd_make_section_with_flags (abfd, ".igot", flags);
      94        if (s == NULL
      95  	  || !bfd_set_section_alignment (s, bed->s->log_file_align))
      96  	return false;
      97        htab->igotplt = s;
      98      }
      99  
     100    return true;
     101  }
     102  
     103  /* Allocate space in .plt, .got and associated reloc sections for
     104     dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
     105  
     106  bool
     107  _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
     108  				    struct elf_link_hash_entry *h,
     109  				    struct elf_dyn_relocs **head,
     110  				    unsigned int plt_entry_size,
     111  				    unsigned int plt_header_size,
     112  				    unsigned int got_entry_size,
     113  				    bool avoid_plt)
     114  {
     115    asection *plt, *gotplt, *relplt;
     116    struct elf_dyn_relocs *p;
     117    unsigned int sizeof_reloc;
     118    const struct elf_backend_data *bed;
     119    struct elf_link_hash_table *htab;
     120    /* If AVOID_PLT is TRUE, don't use PLT if possible.  */
     121    bool use_plt = !avoid_plt || h->plt.refcount > 0;
     122    bool need_dynreloc = !use_plt || bfd_link_pic (info);
     123  
     124    /* When a PIC object references a STT_GNU_IFUNC symbol defined
     125       in executable or it isn't referenced via PLT, the address of
     126       the resolved function may be used.  But in non-PIC executable,
     127       the address of its .plt slot may be used.  Pointer equality may
     128       not work correctly.  PIE or non-PLT reference should be used if
     129       pointer equality is required here.
     130  
     131       If STT_GNU_IFUNC symbol is defined in position-dependent executable,
     132       backend should change it to the normal function and set its address
     133       to its PLT entry which should be resolved by R_*_IRELATIVE at
     134       run-time.  All external references should be resolved to its PLT in
     135       executable.  */
     136    if (!need_dynreloc
     137        && !(bfd_link_pde (info) && h->def_regular)
     138        && (h->dynindx != -1
     139  	  || info->export_dynamic)
     140        && h->pointer_equality_needed)
     141      {
     142        info->callbacks->einfo
     143  	/* xgettext:c-format */
     144  	(_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer "
     145  	   "equality in `%pB' can not be used when making an "
     146  	   "executable; recompile with -fPIE and relink with -pie\n"),
     147  	 h->root.root.string,
     148  	 h->root.u.def.section->owner);
     149        bfd_set_error (bfd_error_bad_value);
     150        return false;
     151      }
     152  
     153    htab = elf_hash_table (info);
     154  
     155    /* When the symbol is marked with regular reference, if PLT isn't used
     156       or we are building a PIC object, we must keep dynamic relocation
     157       if there is non-GOT reference and use PLT if there is PC-relative
     158       reference.  */
     159    if (need_dynreloc && h->ref_regular)
     160      {
     161        bool keep = false;
     162        for (p = *head; p != NULL; p = p->next)
     163  	if (p->count)
     164  	  {
     165  	    h->non_got_ref = 1;
     166  	    /* Need dynamic relocations for non-GOT reference.  */
     167  	    keep = true;
     168  	    if (p->pc_count)
     169  	      {
     170  		/* Must use PLT for PC-relative reference.  */
     171  		use_plt = true;
     172  		need_dynreloc = bfd_link_pic (info);
     173  		break;
     174  	      }
     175  	  }
     176        if (keep)
     177  	goto keep;
     178      }
     179  
     180    /* Support garbage collection against STT_GNU_IFUNC symbols.  */
     181    if (h->plt.refcount <= 0 && h->got.refcount <= 0)
     182      {
     183        h->got = htab->init_got_offset;
     184        h->plt = htab->init_plt_offset;
     185        *head = NULL;
     186        return true;
     187      }
     188  
     189    /* Return and discard space for dynamic relocations against it if
     190       it is never referenced.  */
     191    if (!h->ref_regular)
     192      {
     193        if (h->plt.refcount > 0
     194  	  || h->got.refcount > 0)
     195  	abort ();
     196        h->got = htab->init_got_offset;
     197        h->plt = htab->init_plt_offset;
     198        *head = NULL;
     199        return true;
     200      }
     201  
     202   keep:
     203    bed = get_elf_backend_data (info->output_bfd);
     204    if (bed->rela_plts_and_copies_p)
     205      sizeof_reloc = bed->s->sizeof_rela;
     206    else
     207      sizeof_reloc = bed->s->sizeof_rel;
     208  
     209    /* When building a static executable, use .iplt, .igot.plt and
     210       .rel[a].iplt sections for STT_GNU_IFUNC symbols.  */
     211    if (htab->splt != NULL)
     212      {
     213        plt = htab->splt;
     214        gotplt = htab->sgotplt;
     215        relplt = htab->srelplt;
     216  
     217        /* If this is the first .plt entry and PLT is used, make room for
     218  	 the special first entry.  */
     219        if (plt->size == 0 && use_plt)
     220  	plt->size += plt_header_size;
     221      }
     222    else
     223      {
     224        plt = htab->iplt;
     225        gotplt = htab->igotplt;
     226        relplt = htab->irelplt;
     227      }
     228  
     229    if (use_plt)
     230      {
     231        /* Don't update value of STT_GNU_IFUNC symbol to PLT.  We need
     232  	 the original value for R_*_IRELATIVE.  */
     233        h->plt.offset = plt->size;
     234  
     235        /* Make room for this entry in the .plt/.iplt section.  */
     236        plt->size += plt_entry_size;
     237  
     238        /* We also need to make an entry in the .got.plt/.got.iplt section,
     239  	 which will be placed in the .got section by the linker script.  */
     240        gotplt->size += got_entry_size;
     241      }
     242  
     243    /* We also need to make an entry in the .rel[a].plt/.rel[a].iplt
     244       section for GOTPLT relocation if PLT is used.  */
     245    if (use_plt)
     246      {
     247        relplt->size += sizeof_reloc;
     248        relplt->reloc_count++;
     249      }
     250  
     251    /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
     252       there is a non-GOT reference in a PIC object or PLT isn't used.  */
     253    if (!need_dynreloc || !h->non_got_ref)
     254      *head = NULL;
     255  
     256    /* Finally, allocate space.  */
     257    p = *head;
     258    if (p != NULL)
     259      {
     260        bfd_size_type count = 0;
     261        do
     262  	{
     263  	  count += p->count;
     264  	  p = p->next;
     265  	}
     266        while (p != NULL);
     267  
     268        htab->ifunc_resolvers = count != 0;
     269  
     270        /* Dynamic relocations are stored in
     271  	 1. .rel[a].ifunc section in PIC object.
     272  	 2. .rel[a].got section in dynamic executable.
     273  	 3. .rel[a].iplt section in static executable.  */
     274        if (bfd_link_pic (info))
     275  	htab->irelifunc->size += count * sizeof_reloc;
     276        else if (htab->splt != NULL)
     277  	htab->srelgot->size += count * sizeof_reloc;
     278        else
     279  	{
     280  	  relplt->size += count * sizeof_reloc;
     281  	  relplt->reloc_count += count;
     282  	}
     283      }
     284  
     285    /* For STT_GNU_IFUNC symbol, .got.plt has the real function address
     286       and .got has the PLT entry adddress.  We will load the GOT entry
     287       with the PLT entry in finish_dynamic_symbol if it is used.  For
     288       branch, it uses .got.plt.  For symbol value, if PLT is used,
     289       1. Use .got.plt in a PIC object if it is forced local or not
     290       dynamic.
     291       2. Use .got.plt in a non-PIC object if pointer equality isn't
     292       needed.
     293       3. Use .got.plt in PIE.
     294       4. Use .got.plt if .got isn't used.
     295       5. Otherwise use .got so that it can be shared among different
     296       objects at run-time.
     297       If PLT isn't used, always use .got for symbol value.
     298       We only need to relocate .got entry in PIC object or in dynamic
     299       executable without PLT.  */
     300    if (use_plt
     301        && (h->got.refcount <= 0
     302  	  || (bfd_link_pic (info)
     303  	      && (h->dynindx == -1
     304  		  || h->forced_local))
     305  	  || (!bfd_link_pic (info)
     306  	      && !h->pointer_equality_needed)
     307  	  || bfd_link_pie (info)
     308  	  || htab->sgot == NULL))
     309      {
     310        /* Use .got.plt.  */
     311        h->got.offset = (bfd_vma) -1;
     312      }
     313    else
     314      {
     315        if (!use_plt)
     316  	{
     317  	  /* PLT isn't used.  */
     318  	  h->plt.offset = (bfd_vma) -1;
     319  	}
     320        if (h->got.refcount <= 0)
     321  	{
     322  	  /* GOT isn't need when there are only relocations for static
     323  	     pointers.  */
     324  	  h->got.offset = (bfd_vma) -1;
     325  	}
     326        else
     327  	{
     328  	  h->got.offset = htab->sgot->size;
     329  	  htab->sgot->size += got_entry_size;
     330  	  /* Need to relocate the GOT entry in a PIC object or PLT isn't
     331  	     used.  Otherwise, the GOT entry will be filled with the PLT
     332  	     entry and dynamic GOT relocation isn't needed.  */
     333  	  if (need_dynreloc)
     334  	    {
     335  	      /* For non-static executable, dynamic GOT relocation is in
     336  		 .rel[a].got section, but for static executable, it is
     337  		 in .rel[a].iplt section.  */
     338  	      if (htab->splt != NULL)
     339  		htab->srelgot->size += sizeof_reloc;
     340  	      else
     341  		{
     342  		  relplt->size += sizeof_reloc;
     343  		  relplt->reloc_count++;
     344  		}
     345  	    }
     346  	}
     347      }
     348  
     349    return true;
     350  }