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