mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-11-03 20:53:13 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			331 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Handle loading and unloading shared objects for internal libc purposes.
 | 
						|
   Copyright (C) 1999-2015 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);
 | 
						|
}
 |