hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame elf/dl-libc.c

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