Blame elf/dl-libc.c

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