Blame iconv/gconv_dl.c

Packit 6c4009
/* Handle loading/unloading of shared object for transformation.
Packit 6c4009
   Copyright (C) 1997-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
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 <assert.h>
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <inttypes.h>
Packit 6c4009
#include <search.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
Packit 6c4009
#include <gconv_int.h>
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifdef DEBUG
Packit 6c4009
/* For debugging purposes.  */
Packit 6c4009
static void print_all (void);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is a tuning parameter.  If a transformation module is not used
Packit 6c4009
   anymore it gets not immediately unloaded.  Instead we wait a certain
Packit 6c4009
   number of load attempts for further modules.  If none of the
Packit 6c4009
   subsequent load attempts name the same object it finally gets unloaded.
Packit 6c4009
   Otherwise it is still available which hopefully is the frequent case.
Packit 6c4009
   The following number is the number of unloading attempts we wait
Packit 6c4009
   before unloading.  */
Packit 6c4009
#define TRIES_BEFORE_UNLOAD	2
Packit 6c4009
Packit 6c4009
/* Array of loaded objects.  This is shared by all threads so we have
Packit 6c4009
   to use semaphores to access it.  */
Packit 6c4009
static void *loaded;
Packit 6c4009
Packit 6c4009
/* Comparison function for searching `loaded_object' tree.  */
Packit 6c4009
static int
Packit 6c4009
known_compare (const void *p1, const void *p2)
Packit 6c4009
{
Packit 6c4009
  const struct __gconv_loaded_object *s1 =
Packit 6c4009
    (const struct __gconv_loaded_object *) p1;
Packit 6c4009
  const struct __gconv_loaded_object *s2 =
Packit 6c4009
    (const struct __gconv_loaded_object *) p2;
Packit 6c4009
Packit 6c4009
  return strcmp (s1->name, s2->name);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Open the gconv database if necessary.  A non-negative return value
Packit 6c4009
   means success.  */
Packit 6c4009
struct __gconv_loaded_object *
Packit 6c4009
__gconv_find_shlib (const char *name)
Packit 6c4009
{
Packit 6c4009
  struct __gconv_loaded_object *found;
Packit 6c4009
  void *keyp;
Packit 6c4009
Packit 6c4009
  /* Search the tree of shared objects previously requested.  Data in
Packit 6c4009
     the tree are `loaded_object' structures, whose first member is a
Packit 6c4009
     `const char *', the lookup key.  The search returns a pointer to
Packit 6c4009
     the tree node structure; the first member of the is a pointer to
Packit 6c4009
     our structure (i.e. what will be a `loaded_object'); since the
Packit 6c4009
     first member of that is the lookup key string, &FCT_NAME is close
Packit 6c4009
     enough to a pointer to our structure to use as a lookup key that
Packit 6c4009
     will be passed to `known_compare' (above).  */
Packit 6c4009
Packit 6c4009
  keyp = __tfind (&name, &loaded, known_compare);
Packit 6c4009
  if (keyp == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* This name was not known before.  */
Packit 6c4009
      size_t namelen = strlen (name) + 1;
Packit 6c4009
Packit 6c4009
      found = malloc (sizeof (struct __gconv_loaded_object) + namelen);
Packit 6c4009
      if (found != NULL)
Packit 6c4009
	{
Packit 6c4009
	  /* Point the tree node at this new structure.  */
Packit 6c4009
	  found->name = (char *) memcpy (found + 1, name, namelen);
Packit 6c4009
	  found->counter = -TRIES_BEFORE_UNLOAD - 1;
Packit 6c4009
	  found->handle = NULL;
Packit 6c4009
Packit 6c4009
	  if (__builtin_expect (__tsearch (found, &loaded, known_compare)
Packit 6c4009
				== NULL, 0))
Packit 6c4009
	    {
Packit 6c4009
	      /* Something went wrong while inserting the entry.  */
Packit 6c4009
	      free (found);
Packit 6c4009
	      found = NULL;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    found = *(struct __gconv_loaded_object **) keyp;
Packit 6c4009
Packit 6c4009
  /* Try to load the shared object if the usage count is 0.  This
Packit 6c4009
     implies that if the shared object is not loadable, the handle is
Packit 6c4009
     NULL and the usage count > 0.  */
Packit 6c4009
  if (found != NULL)
Packit 6c4009
    {
Packit 6c4009
      if (found->counter < -TRIES_BEFORE_UNLOAD)
Packit 6c4009
	{
Packit 6c4009
	  assert (found->handle == NULL);
Packit 6c4009
	  found->handle = __libc_dlopen (found->name);
Packit 6c4009
	  if (found->handle != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      found->fct = __libc_dlsym (found->handle, "gconv");
Packit 6c4009
	      if (found->fct == NULL)
Packit 6c4009
		{
Packit 6c4009
		  /* Argh, no conversion function.  There is something
Packit 6c4009
                     wrong here.  */
Packit 6c4009
		  __gconv_release_shlib (found);
Packit 6c4009
		  found = NULL;
Packit 6c4009
		}
Packit 6c4009
	      else
Packit 6c4009
		{
Packit 6c4009
		  found->init_fct = __libc_dlsym (found->handle, "gconv_init");
Packit 6c4009
		  found->end_fct = __libc_dlsym (found->handle, "gconv_end");
Packit 6c4009
Packit 6c4009
#ifdef PTR_MANGLE
Packit 6c4009
		  PTR_MANGLE (found->fct);
Packit 6c4009
		  PTR_MANGLE (found->init_fct);
Packit 6c4009
		  PTR_MANGLE (found->end_fct);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
		  /* We have succeeded in loading the shared object.  */
Packit 6c4009
		  found->counter = 1;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    /* Error while loading the shared object.  */
Packit 6c4009
	    found = NULL;
Packit 6c4009
	}
Packit 6c4009
      else if (found->handle != NULL)
Packit 6c4009
	found->counter = MAX (found->counter + 1, 1);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return found;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This is very ugly but the tsearch functions provide no way to pass
Packit 6c4009
   information to the walker function.  So we use a global variable.
Packit 6c4009
   It is MT safe since we use a lock.  */
Packit 6c4009
static struct __gconv_loaded_object *release_handle;
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
do_release_shlib (void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  struct __gconv_loaded_object *obj = *(struct __gconv_loaded_object **) nodep;
Packit 6c4009
Packit 6c4009
  if (value != preorder && value != leaf)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  if (obj == release_handle)
Packit 6c4009
    {
Packit 6c4009
      /* This is the object we want to unload.  Now decrement the
Packit 6c4009
	 reference counter.  */
Packit 6c4009
      assert (obj->counter > 0);
Packit 6c4009
      --obj->counter;
Packit 6c4009
    }
Packit 6c4009
  else if (obj->counter <= 0 && obj->counter >= -TRIES_BEFORE_UNLOAD
Packit 6c4009
	   && --obj->counter < -TRIES_BEFORE_UNLOAD && obj->handle != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Unload the shared object.  */
Packit 6c4009
      __libc_dlclose (obj->handle);
Packit 6c4009
      obj->handle = NULL;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Notify system that a shared object is not longer needed.  */
Packit 6c4009
void
Packit 6c4009
__gconv_release_shlib (struct __gconv_loaded_object *handle)
Packit 6c4009
{
Packit 6c4009
  /* Urgh, this is ugly but we have no other possibility.  */
Packit 6c4009
  release_handle = handle;
Packit 6c4009
Packit 6c4009
  /* Process all entries.  Please note that we also visit entries
Packit 6c4009
     with release counts <= 0.  This way we can finally unload them
Packit 6c4009
     if necessary.  */
Packit 6c4009
  __twalk (loaded, (__action_fn_t) do_release_shlib);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* We run this if we debug the memory allocation.  */
Packit 6c4009
static void __libc_freeres_fn_section
Packit 6c4009
do_release_all (void *nodep)
Packit 6c4009
{
Packit 6c4009
  struct __gconv_loaded_object *obj = (struct __gconv_loaded_object *) nodep;
Packit 6c4009
Packit 6c4009
  /* Unload the shared object.  */
Packit 6c4009
  if (obj->handle != NULL)
Packit 6c4009
    __libc_dlclose (obj->handle);
Packit 6c4009
Packit 6c4009
  free (obj);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
libc_freeres_fn (free_mem)
Packit 6c4009
{
Packit 6c4009
  __tdestroy (loaded, do_release_all);
Packit 6c4009
  loaded = NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifdef DEBUG
Packit 6c4009
Packit 6c4009
#include <stdio.h>
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
do_print (const void *nodep, VISIT value, int level)
Packit 6c4009
{
Packit 6c4009
  struct __gconv_loaded_object *obj = *(struct __gconv_loaded_object **) nodep;
Packit 6c4009
Packit 6c4009
  printf ("%10s: \"%s\", %d\n",
Packit 6c4009
	  value == leaf ? "leaf" :
Packit 6c4009
	  value == preorder ? "preorder" :
Packit 6c4009
	  value == postorder ? "postorder" : "endorder",
Packit 6c4009
	  obj->name, obj->counter);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void __attribute__ ((used))
Packit 6c4009
print_all (void)
Packit 6c4009
{
Packit 6c4009
  __twalk (loaded, do_print);
Packit 6c4009
}
Packit 6c4009
#endif