| /* Handle loading and unloading shared objects for internal libc purposes. | 
 |    Copyright (C) 1999-2016 Free Software Foundation, Inc. | 
 |    This file is part of the GNU C Library. | 
 |    Contributed by Zack Weinberg <zack@rabi.columbia.edu>, 1999. | 
 |  | 
 |    The GNU C Library is free software; you can redistribute it and/or | 
 |    modify it under the terms of the GNU Lesser General Public | 
 |    License as published by the Free Software Foundation; either | 
 |    version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |    The GNU C Library is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |    Lesser General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU Lesser General Public | 
 |    License along with the GNU C Library; if not, see | 
 |    <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include <dlfcn.h> | 
 | #include <stdlib.h> | 
 | #include <ldsodefs.h> | 
 |  | 
 | extern int __libc_argc attribute_hidden; | 
 | extern char **__libc_argv attribute_hidden; | 
 |  | 
 | extern char **__environ; | 
 |  | 
 | /* The purpose of this file is to provide wrappers around the dynamic | 
 |    linker error mechanism (similar to dlopen() et al in libdl) which | 
 |    are usable from within libc.  Generally we want to throw away the | 
 |    string that dlerror() would return and just pass back a null pointer | 
 |    for errors.  This also lets the rest of libc not know about the error | 
 |    handling mechanism. | 
 |  | 
 |    Much of this code came from gconv_dl.c with slight modifications. */ | 
 |  | 
 | static int | 
 | internal_function | 
 | dlerror_run (void (*operate) (void *), void *args) | 
 | { | 
 |   const char *objname; | 
 |   const char *last_errstring = NULL; | 
 |   bool malloced; | 
 |  | 
 |   int result = (GLRO(dl_catch_error) (&objname, &last_errstring, &malloced, | 
 | 				      operate, args) | 
 | 		?: last_errstring != NULL); | 
 |  | 
 |   if (result && malloced) | 
 |     free ((char *) last_errstring); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | /* These functions are called by dlerror_run... */ | 
 |  | 
 | struct do_dlopen_args | 
 | { | 
 |   /* Argument to do_dlopen.  */ | 
 |   const char *name; | 
 |   /* Opening mode.  */ | 
 |   int mode; | 
 |   /* This is the caller of the dlopen() function.  */ | 
 |   const void *caller_dlopen; | 
 |  | 
 |   /* Return from do_dlopen.  */ | 
 |   struct link_map *map; | 
 | }; | 
 |  | 
 | struct do_dlsym_args | 
 | { | 
 |   /* Arguments to do_dlsym.  */ | 
 |   struct link_map *map; | 
 |   const char *name; | 
 |  | 
 |   /* Return values of do_dlsym.  */ | 
 |   lookup_t loadbase; | 
 |   const ElfW(Sym) *ref; | 
 | }; | 
 |  | 
 | static void | 
 | do_dlopen (void *ptr) | 
 | { | 
 |   struct do_dlopen_args *args = (struct do_dlopen_args *) ptr; | 
 |   /* Open and relocate the shared object.  */ | 
 |   args->map = GLRO(dl_open) (args->name, args->mode, args->caller_dlopen, | 
 | 			     __LM_ID_CALLER, __libc_argc, __libc_argv, | 
 | 			     __environ); | 
 | } | 
 |  | 
 | static void | 
 | do_dlsym (void *ptr) | 
 | { | 
 |   struct do_dlsym_args *args = (struct do_dlsym_args *) ptr; | 
 |   args->ref = NULL; | 
 |   args->loadbase = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref, | 
 | 					     args->map->l_local_scope, NULL, 0, | 
 | 					     DL_LOOKUP_RETURN_NEWEST, NULL); | 
 | } | 
 |  | 
 | static void | 
 | do_dlclose (void *ptr) | 
 | { | 
 |   GLRO(dl_close) ((struct link_map *) ptr); | 
 | } | 
 |  | 
 | /* This code is to support __libc_dlopen from __libc_dlopen'ed shared | 
 |    libraries.  We need to ensure the statically linked __libc_dlopen | 
 |    etc. functions are used instead of the dynamically loaded.  */ | 
 | struct dl_open_hook | 
 | { | 
 |   void *(*dlopen_mode) (const char *name, int mode); | 
 |   void *(*dlsym) (void *map, const char *name); | 
 |   int (*dlclose) (void *map); | 
 | }; | 
 |  | 
 | #ifdef SHARED | 
 | extern struct dl_open_hook *_dl_open_hook; | 
 | libc_hidden_proto (_dl_open_hook); | 
 | struct dl_open_hook *_dl_open_hook __attribute__ ((nocommon)); | 
 | libc_hidden_data_def (_dl_open_hook); | 
 | #else | 
 | static void | 
 | do_dlsym_private (void *ptr) | 
 | { | 
 |   lookup_t l; | 
 |   struct r_found_version vers; | 
 |   vers.name = "GLIBC_PRIVATE"; | 
 |   vers.hidden = 1; | 
 |   /* vers.hash = _dl_elf_hash (vers.name);  */ | 
 |   vers.hash = 0x0963cf85; | 
 |   vers.filename = NULL; | 
 |  | 
 |   struct do_dlsym_args *args = (struct do_dlsym_args *) ptr; | 
 |   args->ref = NULL; | 
 |   l = GLRO(dl_lookup_symbol_x) (args->name, args->map, &args->ref, | 
 | 				args->map->l_scope, &vers, 0, 0, NULL); | 
 |   args->loadbase = l; | 
 | } | 
 |  | 
 | static struct dl_open_hook _dl_open_hook = | 
 |   { | 
 |     .dlopen_mode = __libc_dlopen_mode, | 
 |     .dlsym = __libc_dlsym, | 
 |     .dlclose = __libc_dlclose | 
 |   }; | 
 | #endif | 
 |  | 
 | /* ... and these functions call dlerror_run. */ | 
 |  | 
 | void * | 
 | __libc_dlopen_mode (const char *name, int mode) | 
 | { | 
 |   struct do_dlopen_args args; | 
 |   args.name = name; | 
 |   args.mode = mode; | 
 |   args.caller_dlopen = RETURN_ADDRESS (0); | 
 |  | 
 | #ifdef SHARED | 
 |   if (__glibc_unlikely (_dl_open_hook != NULL)) | 
 |     return _dl_open_hook->dlopen_mode (name, mode); | 
 |   return (dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map); | 
 | #else | 
 |   if (dlerror_run (do_dlopen, &args)) | 
 |     return NULL; | 
 |  | 
 |   __libc_register_dl_open_hook (args.map); | 
 |   __libc_register_dlfcn_hook (args.map); | 
 |   return (void *) args.map; | 
 | #endif | 
 | } | 
 | libc_hidden_def (__libc_dlopen_mode) | 
 |  | 
 | #ifndef SHARED | 
 | void * | 
 | __libc_dlsym_private (struct link_map *map, const char *name) | 
 | { | 
 |   struct do_dlsym_args sargs; | 
 |   sargs.map = map; | 
 |   sargs.name = name; | 
 |  | 
 |   if (! dlerror_run (do_dlsym_private, &sargs)) | 
 |     return DL_SYMBOL_ADDRESS (sargs.loadbase, sargs.ref); | 
 |   return NULL; | 
 | } | 
 |  | 
 | void | 
 | __libc_register_dl_open_hook (struct link_map *map) | 
 | { | 
 |   struct dl_open_hook **hook; | 
 |  | 
 |   hook = (struct dl_open_hook **) __libc_dlsym_private (map, "_dl_open_hook"); | 
 |   if (hook != NULL) | 
 |     *hook = &_dl_open_hook; | 
 | } | 
 | #endif | 
 |  | 
 | void * | 
 | __libc_dlsym (void *map, const char *name) | 
 | { | 
 |   struct do_dlsym_args args; | 
 |   args.map = map; | 
 |   args.name = name; | 
 |  | 
 | #ifdef SHARED | 
 |   if (__glibc_unlikely (_dl_open_hook != NULL)) | 
 |     return _dl_open_hook->dlsym (map, name); | 
 | #endif | 
 |   return (dlerror_run (do_dlsym, &args) ? NULL | 
 | 	  : (void *) (DL_SYMBOL_ADDRESS (args.loadbase, args.ref))); | 
 | } | 
 | libc_hidden_def (__libc_dlsym) | 
 |  | 
 | int | 
 | __libc_dlclose (void *map) | 
 | { | 
 | #ifdef SHARED | 
 |   if (__glibc_unlikely (_dl_open_hook != NULL)) | 
 |     return _dl_open_hook->dlclose (map); | 
 | #endif | 
 |   return dlerror_run (do_dlclose, map); | 
 | } | 
 | libc_hidden_def (__libc_dlclose) | 
 |  | 
 |  | 
 | static bool __libc_freeres_fn_section | 
 | free_slotinfo (struct dtv_slotinfo_list **elemp) | 
 | { | 
 |   size_t cnt; | 
 |  | 
 |   if (*elemp == NULL) | 
 |     /* Nothing here, all is removed (or there never was anything).  */ | 
 |     return true; | 
 |  | 
 |   if (!free_slotinfo (&(*elemp)->next)) | 
 |     /* We cannot free the entry.  */ | 
 |     return false; | 
 |  | 
 |   /* That cleared our next pointer for us.  */ | 
 |  | 
 |   for (cnt = 0; cnt < (*elemp)->len; ++cnt) | 
 |     if ((*elemp)->slotinfo[cnt].map != NULL) | 
 |       /* Still used.  */ | 
 |       return false; | 
 |  | 
 |   /* We can remove the list element.  */ | 
 |   free (*elemp); | 
 |   *elemp = NULL; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 |  | 
 | libc_freeres_fn (free_mem) | 
 | { | 
 |   struct link_map *l; | 
 |   struct r_search_path_elem *d; | 
 |  | 
 |   /* Remove all search directories.  */ | 
 |   d = GL(dl_all_dirs); | 
 |   while (d != GLRO(dl_init_all_dirs)) | 
 |     { | 
 |       struct r_search_path_elem *old = d; | 
 |       d = d->next; | 
 |       free (old); | 
 |     } | 
 |  | 
 |   for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) | 
 |     { | 
 |       for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) | 
 | 	{ | 
 | 	  struct libname_list *lnp = l->l_libname->next; | 
 |  | 
 | 	  l->l_libname->next = NULL; | 
 |  | 
 | 	  /* Remove all additional names added to the objects.  */ | 
 | 	  while (lnp != NULL) | 
 | 	    { | 
 | 	      struct libname_list *old = lnp; | 
 | 	      lnp = lnp->next; | 
 | 	      if (! old->dont_free) | 
 | 		free (old); | 
 | 	    } | 
 |  | 
 | 	  /* Free the initfini dependency list.  */ | 
 | 	  if (l->l_free_initfini) | 
 | 	    free (l->l_initfini); | 
 | 	  l->l_initfini = NULL; | 
 | 	} | 
 |  | 
 |       if (__builtin_expect (GL(dl_ns)[ns]._ns_global_scope_alloc, 0) != 0 | 
 | 	  && (GL(dl_ns)[ns]._ns_main_searchlist->r_nlist | 
 | 	      // XXX Check whether we need NS-specific initial_searchlist | 
 | 	      == GLRO(dl_initial_searchlist).r_nlist)) | 
 | 	{ | 
 | 	  /* All object dynamically loaded by the program are unloaded.  Free | 
 | 	     the memory allocated for the global scope variable.  */ | 
 | 	  struct link_map **old = GL(dl_ns)[ns]._ns_main_searchlist->r_list; | 
 |  | 
 | 	  /* Put the old map in.  */ | 
 | 	  GL(dl_ns)[ns]._ns_main_searchlist->r_list | 
 | 	    // XXX Check whether we need NS-specific initial_searchlist | 
 | 	    = GLRO(dl_initial_searchlist).r_list; | 
 | 	  /* Signal that the original map is used.  */ | 
 | 	  GL(dl_ns)[ns]._ns_global_scope_alloc = 0; | 
 |  | 
 | 	  /* Now free the old map.  */ | 
 | 	  free (old); | 
 | 	} | 
 |     } | 
 |  | 
 |   /* Free the memory allocated for the dtv slotinfo array.  We can do | 
 |      this only if all modules which used this memory are unloaded.  */ | 
 | #ifdef SHARED | 
 |   if (GL(dl_initial_dtv) == NULL) | 
 |     /* There was no initial TLS setup, it was set up later when | 
 |        it used the normal malloc.  */ | 
 |     free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)); | 
 |   else | 
 | #endif | 
 |     /* The first element of the list does not have to be deallocated. | 
 |        It was allocated in the dynamic linker (i.e., with a different | 
 |        malloc), and in the static library it's in .bss space.  */ | 
 |     free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)->next); | 
 |  | 
 |   void *scope_free_list = GL(dl_scope_free_list); | 
 |   GL(dl_scope_free_list) = NULL; | 
 |   free (scope_free_list); | 
 | } |