(root)/
glibc-2.38/
elf/
dl-libc.c
       1  /* Handle loading and unloading shared objects for internal libc purposes.
       2     Copyright (C) 1999-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 <dlfcn.h>
      20  #include <stdlib.h>
      21  #include <ldsodefs.h>
      22  #include <dl-hash.h>
      23  
      24  extern int __libc_argc attribute_hidden;
      25  extern char **__libc_argv attribute_hidden;
      26  
      27  extern char **__environ;
      28  
      29  /* The purpose of this file is to provide wrappers around the dynamic
      30     linker error mechanism (similar to dlopen() et al in libdl) which
      31     are usable from within libc.  Generally we want to throw away the
      32     string that dlerror() would return and just pass back a null pointer
      33     for errors.  This also lets the rest of libc not know about the error
      34     handling mechanism.
      35  
      36     Much of this code came from gconv_dl.c with slight modifications. */
      37  
      38  static int
      39  dlerror_run (void (*operate) (void *), void *args)
      40  {
      41    const char *objname;
      42    const char *last_errstring = NULL;
      43    bool malloced;
      44  
      45    int result = (GLRO (dl_catch_error) (&objname, &last_errstring, &malloced,
      46  				       operate, args)
      47  		?: last_errstring != NULL);
      48  
      49    if (result && malloced)
      50      GLRO (dl_error_free) ((char *) last_errstring);
      51  
      52    return result;
      53  }
      54  
      55  /* These functions are called by dlerror_run... */
      56  
      57  struct do_dlopen_args
      58  {
      59    /* Argument to do_dlopen.  */
      60    const char *name;
      61    /* Opening mode.  */
      62    int mode;
      63    /* This is the caller of the dlopen() function.  */
      64    const void *caller_dlopen;
      65  
      66    /* Return from do_dlopen.  */
      67    struct link_map *map;
      68  };
      69  
      70  struct do_dlsym_args
      71  {
      72    /* Arguments to do_dlsym.  */
      73    struct link_map *map;
      74    const char *name;
      75  
      76    /* Return values of do_dlsym.  */
      77    lookup_t loadbase;
      78    const ElfW(Sym) *ref;
      79  };
      80  
      81  struct do_dlvsym_args
      82  {
      83    /* dlvsym is like dlsym.  */
      84    struct do_dlsym_args dlsym;
      85  
      86    /* But dlvsym needs a version  as well.  */
      87    struct r_found_version version;
      88  };
      89  
      90  static void
      91  do_dlopen (void *ptr)
      92  {
      93    struct do_dlopen_args *args = (struct do_dlopen_args *) ptr;
      94    /* Open and relocate the shared object.  */
      95    args->map = GLRO(dl_open) (args->name, args->mode, args->caller_dlopen,
      96  			     __LM_ID_CALLER, __libc_argc, __libc_argv,
      97  			     __environ);
      98  }
      99  
     100  static void
     101  do_dlsym (void *ptr)
     102  {
     103    struct do_dlsym_args *args = (struct do_dlsym_args *) ptr;
     104    args->ref = NULL;
     105    args->loadbase = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref,
     106  					     args->map->l_local_scope, NULL, 0,
     107  					     DL_LOOKUP_RETURN_NEWEST, NULL);
     108  }
     109  
     110  static void
     111  do_dlvsym (void *ptr)
     112  {
     113    struct do_dlvsym_args *args = ptr;
     114    args->dlsym.ref = NULL;
     115    args->dlsym.loadbase
     116      = GLRO(dl_lookup_symbol_x) (args->dlsym.name, args->dlsym.map,
     117  				&args->dlsym.ref,
     118  				args->dlsym.map->l_local_scope,
     119  				&args->version, 0, 0, NULL);
     120  }
     121  
     122  static void
     123  do_dlclose (void *ptr)
     124  {
     125    GLRO(dl_close) ((struct link_map *) ptr);
     126  }
     127  
     128  #ifndef SHARED
     129  static void
     130  do_dlsym_private (void *ptr)
     131  {
     132    lookup_t l;
     133    struct r_found_version vers;
     134    vers.name = "GLIBC_PRIVATE";
     135    vers.hidden = 1;
     136    /* vers.hash = _dl_elf_hash (vers.name);  */
     137    vers.hash = 0x0963cf85;
     138    vers.filename = NULL;
     139  
     140    struct do_dlsym_args *args = (struct do_dlsym_args *) ptr;
     141    args->ref = NULL;
     142    l = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref,
     143  				args->map->l_scope, &vers, 0, 0, NULL);
     144    args->loadbase = l;
     145  }
     146  #endif
     147  
     148  /* ... and these functions call dlerror_run. */
     149  
     150  void *
     151  __libc_dlopen_mode (const char *name, int mode)
     152  {
     153    struct do_dlopen_args args;
     154    args.name = name;
     155    args.mode = mode;
     156    args.caller_dlopen = RETURN_ADDRESS (0);
     157  
     158  #ifdef SHARED
     159    if (GLRO (dl_dlfcn_hook) != NULL)
     160      return GLRO (dl_dlfcn_hook)->libc_dlopen_mode (name, mode);
     161  #endif
     162    return dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map;
     163  }
     164  
     165  #ifndef SHARED
     166  void *
     167  __libc_dlsym_private (struct link_map *map, const char *name)
     168  {
     169    struct do_dlsym_args sargs;
     170    sargs.map = map;
     171    sargs.name = name;
     172  
     173    if (! dlerror_run (do_dlsym_private, &sargs))
     174      return DL_SYMBOL_ADDRESS (sargs.loadbase, sargs.ref);
     175    return NULL;
     176  }
     177  #endif
     178  
     179  void *
     180  __libc_dlsym (void *map, const char *name)
     181  {
     182    struct do_dlsym_args args;
     183    args.map = map;
     184    args.name = name;
     185  
     186  #ifdef SHARED
     187    if (GLRO (dl_dlfcn_hook) != NULL)
     188      return GLRO (dl_dlfcn_hook)->libc_dlsym (map, name);
     189  #endif
     190    return (dlerror_run (do_dlsym, &args) ? NULL
     191  	  : (void *) (DL_SYMBOL_ADDRESS (args.loadbase, args.ref)));
     192  }
     193  
     194  /* Replacement for dlvsym.  MAP must be a real map.  This function
     195     returns NULL without setting the dlerror value in case of static
     196     dlopen from an old binary.  */
     197  void *
     198  __libc_dlvsym (void *map, const char *name, const char *version)
     199  {
     200  #ifdef SHARED
     201    if (GLRO (dl_dlfcn_hook) != NULL)
     202      return GLRO (dl_dlfcn_hook)->libc_dlvsym (map, name, version);
     203  #endif
     204  
     205    struct do_dlvsym_args args;
     206    args.dlsym.map = map;
     207    args.dlsym.name = name;
     208  
     209    /* See _dl_vsym in dl-sym.c.  */
     210    args.version.name = version;
     211    args.version.hidden = 1;
     212    args.version.hash = _dl_elf_hash (version);
     213    args.version.filename = NULL;
     214  
     215    return (dlerror_run (do_dlvsym, &args) ? NULL
     216  	  : (void *) (DL_SYMBOL_ADDRESS (args.dlsym.loadbase,
     217  					 args.dlsym.ref)));
     218  }
     219  
     220  int
     221  __libc_dlclose (void *map)
     222  {
     223  #ifdef SHARED
     224    if (GLRO (dl_dlfcn_hook) != NULL)
     225      return GLRO (dl_dlfcn_hook)->libc_dlclose (map);
     226  #endif
     227    return dlerror_run (do_dlclose, map);
     228  }
     229  
     230  
     231  static bool
     232  free_slotinfo (struct dtv_slotinfo_list **elemp)
     233  {
     234    size_t cnt;
     235  
     236    if (*elemp == NULL)
     237      /* Nothing here, all is removed (or there never was anything).  */
     238      return true;
     239  
     240    if (!free_slotinfo (&(*elemp)->next))
     241      /* We cannot free the entry.  */
     242      return false;
     243  
     244    /* That cleared our next pointer for us.  */
     245  
     246    for (cnt = 0; cnt < (*elemp)->len; ++cnt)
     247      if ((*elemp)->slotinfo[cnt].map != NULL)
     248        /* Still used.  */
     249        return false;
     250  
     251    /* We can remove the list element.  */
     252    free (*elemp);
     253    *elemp = NULL;
     254  
     255    return true;
     256  }
     257  
     258  
     259  void
     260  __dl_libc_freemem (void)
     261  {
     262    struct link_map *l;
     263    struct r_search_path_elem *d;
     264  
     265    /* Remove all search directories.  */
     266    d = GL(dl_all_dirs);
     267    while (d != GLRO(dl_init_all_dirs))
     268      {
     269        struct r_search_path_elem *old = d;
     270        d = d->next;
     271        free (old);
     272      }
     273  
     274    for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
     275      {
     276        for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next)
     277  	{
     278  	  struct libname_list *lnp = l->l_libname->next;
     279  
     280  	  l->l_libname->next = NULL;
     281  
     282  	  /* Remove all additional names added to the objects.  */
     283  	  while (lnp != NULL)
     284  	    {
     285  	      struct libname_list *old = lnp;
     286  	      lnp = lnp->next;
     287  	      if (! old->dont_free)
     288  		free (old);
     289  	    }
     290  
     291  	  /* Free the initfini dependency list.  */
     292  	  if (l->l_free_initfini)
     293  	    free (l->l_initfini);
     294  	  l->l_initfini = NULL;
     295  	}
     296  
     297        if (__builtin_expect (GL(dl_ns)[ns]._ns_global_scope_alloc, 0) != 0
     298  	  && (GL(dl_ns)[ns]._ns_main_searchlist->r_nlist
     299  	      // XXX Check whether we need NS-specific initial_searchlist
     300  	      == GLRO(dl_initial_searchlist).r_nlist))
     301  	{
     302  	  /* All object dynamically loaded by the program are unloaded.  Free
     303  	     the memory allocated for the global scope variable.  */
     304  	  struct link_map **old = GL(dl_ns)[ns]._ns_main_searchlist->r_list;
     305  
     306  	  /* Put the old map in.  */
     307  	  GL(dl_ns)[ns]._ns_main_searchlist->r_list
     308  	    // XXX Check whether we need NS-specific initial_searchlist
     309  	    = GLRO(dl_initial_searchlist).r_list;
     310  	  /* Signal that the original map is used.  */
     311  	  GL(dl_ns)[ns]._ns_global_scope_alloc = 0;
     312  
     313  	  /* Now free the old map.  */
     314  	  free (old);
     315  	}
     316      }
     317  
     318    /* Free the memory allocated for the dtv slotinfo array.  We can do
     319       this only if all modules which used this memory are unloaded.  */
     320  #ifdef SHARED
     321    if (GL(dl_initial_dtv) == NULL)
     322      /* There was no initial TLS setup, it was set up later when
     323         it used the normal malloc.  */
     324      free_slotinfo (&GL(dl_tls_dtv_slotinfo_list));
     325    else
     326  #endif
     327      /* The first element of the list does not have to be deallocated.
     328         It was allocated in the dynamic linker (i.e., with a different
     329         malloc), and in the static library it's in .bss space.  */
     330      free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)->next);
     331  
     332    void *scope_free_list = GL(dl_scope_free_list);
     333    GL(dl_scope_free_list) = NULL;
     334    free (scope_free_list);
     335  }