(root)/
glibc-2.38/
elf/
dl-runtime.c
       1  /* On-demand PLT fixup for shared objects.
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <alloca.h>
      20  #include <assert.h>
      21  #include <stdlib.h>
      22  #include <unistd.h>
      23  #include <sys/param.h>
      24  #include <ldsodefs.h>
      25  #include <sysdep-cancel.h>
      26  #include "dynamic-link.h"
      27  #include <tls.h>
      28  #include <dl-irel.h>
      29  #include <dl-runtime.h>
      30  
      31  
      32  /* This function is called through a special trampoline from the PLT the
      33     first time each PLT entry is called.  We must perform the relocation
      34     specified in the PLT of the given shared object, and return the resolved
      35     function address to the trampoline, which will restart the original call
      36     to that address.  Future calls will bounce directly from the PLT to the
      37     function.  */
      38  
      39  DL_FIXUP_VALUE_TYPE
      40  attribute_hidden __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE
      41  _dl_fixup (
      42  # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
      43  	   ELF_MACHINE_RUNTIME_FIXUP_ARGS,
      44  # endif
      45  	   struct link_map *l, ElfW(Word) reloc_arg)
      46  {
      47    const ElfW(Sym) *const symtab
      48      = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
      49    const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
      50  
      51    const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
      52  
      53    const PLTREL *const reloc
      54      = (const void *) (D_PTR (l, l_info[DT_JMPREL])
      55  		      + reloc_offset (pltgot, reloc_arg));
      56    const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
      57    const ElfW(Sym) *refsym = sym;
      58    void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
      59    lookup_t result;
      60    DL_FIXUP_VALUE_TYPE value;
      61  
      62    /* Sanity check that we're really looking at a PLT relocation.  */
      63    assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
      64  
      65     /* Look up the target symbol.  If the normal lookup rules are not
      66        used don't look in the global scope.  */
      67    if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
      68      {
      69        const struct r_found_version *version = NULL;
      70  
      71        if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
      72  	{
      73  	  const ElfW(Half) *vernum =
      74  	    (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
      75  	  ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
      76  	  version = &l->l_versions[ndx];
      77  	  if (version->hash == 0)
      78  	    version = NULL;
      79  	}
      80  
      81        /* We need to keep the scope around so do some locking.  This is
      82  	 not necessary for objects which cannot be unloaded or when
      83  	 we are not using any threads (yet).  */
      84        int flags = DL_LOOKUP_ADD_DEPENDENCY;
      85        if (!RTLD_SINGLE_THREAD_P)
      86  	{
      87  	  THREAD_GSCOPE_SET_FLAG ();
      88  	  flags |= DL_LOOKUP_GSCOPE_LOCK;
      89  	}
      90  
      91  #ifdef RTLD_ENABLE_FOREIGN_CALL
      92        RTLD_ENABLE_FOREIGN_CALL;
      93  #endif
      94  
      95        result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
      96  				    version, ELF_RTYPE_CLASS_PLT, flags, NULL);
      97  
      98        /* We are done with the global scope.  */
      99        if (!RTLD_SINGLE_THREAD_P)
     100  	THREAD_GSCOPE_RESET_FLAG ();
     101  
     102  #ifdef RTLD_FINALIZE_FOREIGN_CALL
     103        RTLD_FINALIZE_FOREIGN_CALL;
     104  #endif
     105  
     106        /* Currently result contains the base load address (or link map)
     107  	 of the object that defines sym.  Now add in the symbol
     108  	 offset.  */
     109        value = DL_FIXUP_MAKE_VALUE (result,
     110  				   SYMBOL_ADDRESS (result, sym, false));
     111      }
     112    else
     113      {
     114        /* We already found the symbol.  The module (and therefore its load
     115  	 address) is also known.  */
     116        value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, sym, true));
     117        result = l;
     118      }
     119  
     120    /* And now perhaps the relocation addend.  */
     121    value = elf_machine_plt_value (l, reloc, value);
     122  
     123    if (sym != NULL
     124        && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
     125      value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
     126  
     127  #ifdef SHARED
     128    /* Auditing checkpoint: we have a new binding.  Provide the auditing
     129       libraries the possibility to change the value and tell us whether further
     130       auditing is wanted.
     131       The l_reloc_result is only allocated if there is an audit module which
     132       provides a la_symbind.  */
     133    if (l->l_reloc_result != NULL)
     134      {
     135        /* This is the address in the array where we store the result of previous
     136  	 relocations.  */
     137        struct reloc_result *reloc_result
     138  	= &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
     139        unsigned int init = atomic_load_acquire (&reloc_result->init);
     140        if (init == 0)
     141  	{
     142  	  _dl_audit_symbind (l, reloc_result, reloc, sym, &value, result, true);
     143  
     144  	  /* Store the result for later runs.  */
     145  	  if (__glibc_likely (! GLRO(dl_bind_not)))
     146  	    {
     147  	      reloc_result->addr = value;
     148  	      /* Guarantee all previous writes complete before init is
     149  		 updated.  See CONCURRENCY NOTES below.  */
     150  	      atomic_store_release (&reloc_result->init, 1);
     151  	    }
     152  	}
     153        else
     154  	value = reloc_result->addr;
     155      }
     156  #endif
     157  
     158    /* Finally, fix up the plt itself.  */
     159    if (__glibc_unlikely (GLRO(dl_bind_not)))
     160      return value;
     161  
     162    return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);
     163  }
     164  
     165  #ifndef PROF
     166  DL_FIXUP_VALUE_TYPE
     167  __attribute ((noinline))
     168  DL_ARCH_FIXUP_ATTRIBUTE
     169  _dl_profile_fixup (
     170  #ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
     171  		   ELF_MACHINE_RUNTIME_FIXUP_ARGS,
     172  #endif
     173  		   struct link_map *l, ElfW(Word) reloc_arg,
     174  		   ElfW(Addr) retaddr, void *regs, long int *framesizep)
     175  {
     176    void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = _dl_mcount;
     177  
     178    if (l->l_reloc_result == NULL)
     179      {
     180        /* BZ #14843: ELF_DYNAMIC_RELOCATE is called before l_reloc_result
     181  	 is allocated.  We will get here if ELF_DYNAMIC_RELOCATE calls a
     182  	 resolver function to resolve an IRELATIVE relocation and that
     183  	 resolver calls a function that is not yet resolved (lazy).  For
     184  	 example, the resolver in x86-64 libm.so calls __get_cpu_features
     185  	 defined in libc.so.  Skip audit and resolve the external function
     186  	 in this case.  */
     187        *framesizep = -1;
     188        return _dl_fixup (
     189  # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
     190  #  ifndef ELF_MACHINE_RUNTIME_FIXUP_PARAMS
     191  #   error Please define ELF_MACHINE_RUNTIME_FIXUP_PARAMS.
     192  #  endif
     193  			ELF_MACHINE_RUNTIME_FIXUP_PARAMS,
     194  # endif
     195  			l, reloc_arg);
     196      }
     197  
     198    const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
     199  
     200    /* This is the address in the array where we store the result of previous
     201       relocations.  */
     202    struct reloc_result *reloc_result
     203      = &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
     204  
     205   /* CONCURRENCY NOTES:
     206  
     207    Multiple threads may be calling the same PLT sequence and with
     208    LD_AUDIT enabled they will be calling into _dl_profile_fixup to
     209    update the reloc_result with the result of the lazy resolution.
     210    The reloc_result guard variable is reloc_init, and we use
     211    acquire/release loads and store to it to ensure that the results of
     212    the structure are consistent with the loaded value of the guard.
     213    This does not fix all of the data races that occur when two or more
     214    threads read reloc_result->reloc_init with a value of zero and read
     215    and write to that reloc_result concurrently.  The expectation is
     216    generally that while this is a data race it works because the
     217    threads write the same values.  Until the data races are fixed
     218    there is a potential for problems to arise from these data races.
     219    The reloc result updates should happen in parallel but there should
     220    be an atomic RMW which does the final update to the real result
     221    entry (see bug 23790).
     222  
     223    The following code uses reloc_result->init set to 0 to indicate if it is
     224    the first time this object is being relocated, otherwise 1 which
     225    indicates the object has already been relocated.
     226  
     227    Reading/Writing from/to reloc_result->reloc_init must not happen
     228    before previous writes to reloc_result complete as they could
     229    end-up with an incomplete struct.  */
     230    DL_FIXUP_VALUE_TYPE value;
     231    unsigned int init = atomic_load_acquire (&reloc_result->init);
     232  
     233    if (init == 0)
     234      {
     235        /* This is the first time we have to relocate this object.  */
     236        const ElfW(Sym) *const symtab
     237  	= (const void *) D_PTR (l, l_info[DT_SYMTAB]);
     238        const char *strtab = (const char *) D_PTR (l, l_info[DT_STRTAB]);
     239  
     240        const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]);
     241  
     242        const PLTREL *const reloc
     243  	= (const void *) (D_PTR (l, l_info[DT_JMPREL])
     244  			  + reloc_offset (pltgot, reloc_arg));
     245        const ElfW(Sym) *refsym = &symtab[ELFW(R_SYM) (reloc->r_info)];
     246        const ElfW(Sym) *defsym = refsym;
     247        lookup_t result;
     248  
     249        /* Sanity check that we're really looking at a PLT relocation.  */
     250        assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
     251  
     252        /* Look up the target symbol.  If the symbol is marked STV_PROTECTED
     253  	 don't look in the global scope.  */
     254        if (__builtin_expect (ELFW(ST_VISIBILITY) (refsym->st_other), 0) == 0)
     255  	{
     256  	  const struct r_found_version *version = NULL;
     257  
     258  	  if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
     259  	    {
     260  	      const ElfW(Half) *vernum =
     261  		(const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
     262  	      ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
     263  	      version = &l->l_versions[ndx];
     264  	      if (version->hash == 0)
     265  		version = NULL;
     266  	    }
     267  
     268  	  /* We need to keep the scope around so do some locking.  This is
     269  	     not necessary for objects which cannot be unloaded or when
     270  	     we are not using any threads (yet).  */
     271  	  int flags = DL_LOOKUP_ADD_DEPENDENCY;
     272  	  if (!RTLD_SINGLE_THREAD_P)
     273  	    {
     274  	      THREAD_GSCOPE_SET_FLAG ();
     275  	      flags |= DL_LOOKUP_GSCOPE_LOCK;
     276  	    }
     277  
     278  	  result = _dl_lookup_symbol_x (strtab + refsym->st_name, l,
     279  					&defsym, l->l_scope, version,
     280  					ELF_RTYPE_CLASS_PLT, flags, NULL);
     281  
     282  	  /* We are done with the global scope.  */
     283  	  if (!RTLD_SINGLE_THREAD_P)
     284  	    THREAD_GSCOPE_RESET_FLAG ();
     285  
     286  	  /* Currently result contains the base load address (or link map)
     287  	     of the object that defines sym.  Now add in the symbol
     288  	     offset.  */
     289  	  value = DL_FIXUP_MAKE_VALUE (result,
     290  				       SYMBOL_ADDRESS (result, defsym, false));
     291  
     292  	  if (defsym != NULL
     293  	      && __builtin_expect (ELFW(ST_TYPE) (defsym->st_info)
     294  				   == STT_GNU_IFUNC, 0))
     295  	    value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
     296  	}
     297        else
     298  	{
     299  	  /* We already found the symbol.  The module (and therefore its load
     300  	     address) is also known.  */
     301  	  value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, refsym, true));
     302  
     303  	  if (__builtin_expect (ELFW(ST_TYPE) (refsym->st_info)
     304  				== STT_GNU_IFUNC, 0))
     305  	    value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
     306  
     307  	  result = l;
     308  	}
     309        /* And now perhaps the relocation addend.  */
     310        value = elf_machine_plt_value (l, reloc, value);
     311  
     312  #ifdef SHARED
     313        /* Auditing checkpoint: we have a new binding.  Provide the
     314  	 auditing libraries the possibility to change the value and
     315  	 tell us whether further auditing is wanted.  */
     316        if (defsym != NULL && GLRO(dl_naudit) > 0)
     317  	_dl_audit_symbind (l, reloc_result, reloc, defsym, &value, result,
     318  			   true);
     319  #endif
     320  
     321        /* Store the result for later runs.  */
     322        if (__glibc_likely (! GLRO(dl_bind_not)))
     323  	{
     324  	  reloc_result->addr = value;
     325  	  /* Guarantee all previous writes complete before
     326  	     init is updated.  See CONCURRENCY NOTES earlier  */
     327  	  atomic_store_release (&reloc_result->init, 1);
     328  	}
     329        init = 1;
     330      }
     331    else
     332      value = reloc_result->addr;
     333  
     334    /* By default we do not call the pltexit function.  */
     335    long int framesize = -1;
     336  
     337  
     338  #ifdef SHARED
     339    /* Auditing checkpoint: report the PLT entering and allow the
     340       auditors to change the value.  */
     341    _dl_audit_pltenter (l, reloc_result, &value, regs, &framesize);
     342  #endif
     343  
     344    /* Store the frame size information.  */
     345    *framesizep = framesize;
     346  
     347    (*mcount_fct) (retaddr, DL_FIXUP_VALUE_CODE_ADDR (value));
     348  
     349    return value;
     350  }
     351  
     352  #endif /* PROF */