Back to index

glibc  2.9
gconv_dl.c
Go to the documentation of this file.
00001 /* Handle loading/unloading of shared object for transformation.
00002    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005
00003    Free Software Foundation, Inc.
00004    This file is part of the GNU C Library.
00005    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
00006 
00007    The GNU C Library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser General Public
00009    License as published by the Free Software Foundation; either
00010    version 2.1 of the License, or (at your option) any later version.
00011 
00012    The GNU C Library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Lesser General Public License for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public
00018    License along with the GNU C Library; if not, write to the Free
00019    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00020    02111-1307 USA.  */
00021 
00022 #include <assert.h>
00023 #include <dlfcn.h>
00024 #include <inttypes.h>
00025 #include <search.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <bits/libc-lock.h>
00029 #include <sys/param.h>
00030 
00031 #include <gconv_int.h>
00032 #include <sysdep.h>
00033 
00034 
00035 #ifdef DEBUG
00036 /* For debugging purposes.  */
00037 static void print_all (void);
00038 #endif
00039 
00040 
00041 /* This is a tuning parameter.  If a transformation module is not used
00042    anymore it gets not immediately unloaded.  Instead we wait a certain
00043    number of load attempts for further modules.  If none of the
00044    subsequent load attempts name the same object it finally gets unloaded.
00045    Otherwise it is still available which hopefully is the frequent case.
00046    The following number is the number of unloading attempts we wait
00047    before unloading.  */
00048 #define TRIES_BEFORE_UNLOAD 2
00049 
00050 /* Array of loaded objects.  This is shared by all threads so we have
00051    to use semaphores to access it.  */
00052 static void *loaded;
00053 
00054 /* Comparison function for searching `loaded_object' tree.  */
00055 static int
00056 known_compare (const void *p1, const void *p2)
00057 {
00058   const struct __gconv_loaded_object *s1 =
00059     (const struct __gconv_loaded_object *) p1;
00060   const struct __gconv_loaded_object *s2 =
00061     (const struct __gconv_loaded_object *) p2;
00062 
00063   return strcmp (s1->name, s2->name);
00064 }
00065 
00066 /* Open the gconv database if necessary.  A non-negative return value
00067    means success.  */
00068 struct __gconv_loaded_object *
00069 internal_function
00070 __gconv_find_shlib (const char *name)
00071 {
00072   struct __gconv_loaded_object *found;
00073   void *keyp;
00074 
00075   /* Search the tree of shared objects previously requested.  Data in
00076      the tree are `loaded_object' structures, whose first member is a
00077      `const char *', the lookup key.  The search returns a pointer to
00078      the tree node structure; the first member of the is a pointer to
00079      our structure (i.e. what will be a `loaded_object'); since the
00080      first member of that is the lookup key string, &FCT_NAME is close
00081      enough to a pointer to our structure to use as a lookup key that
00082      will be passed to `known_compare' (above).  */
00083 
00084   keyp = __tfind (&name, &loaded, known_compare);
00085   if (keyp == NULL)
00086     {
00087       /* This name was not known before.  */
00088       size_t namelen = strlen (name) + 1;
00089 
00090       found = malloc (sizeof (struct __gconv_loaded_object) + namelen);
00091       if (found != NULL)
00092        {
00093          /* Point the tree node at this new structure.  */
00094          found->name = (char *) memcpy (found + 1, name, namelen);
00095          found->counter = -TRIES_BEFORE_UNLOAD - 1;
00096          found->handle = NULL;
00097 
00098          if (__builtin_expect (__tsearch (found, &loaded, known_compare)
00099                             == NULL, 0))
00100            {
00101              /* Something went wrong while inserting the entry.  */
00102              free (found);
00103              found = NULL;
00104            }
00105        }
00106     }
00107   else
00108     found = *(struct __gconv_loaded_object **) keyp;
00109 
00110   /* Try to load the shared object if the usage count is 0.  This
00111      implies that if the shared object is not loadable, the handle is
00112      NULL and the usage count > 0.  */
00113   if (found != NULL)
00114     {
00115       if (found->counter < -TRIES_BEFORE_UNLOAD)
00116        {
00117          assert (found->handle == NULL);
00118          found->handle = __libc_dlopen (found->name);
00119          if (found->handle != NULL)
00120            {
00121              found->fct = __libc_dlsym (found->handle, "gconv");
00122              if (found->fct == NULL)
00123               {
00124                 /* Argh, no conversion function.  There is something
00125                      wrong here.  */
00126                 __gconv_release_shlib (found);
00127                 found = NULL;
00128               }
00129              else
00130               {
00131                 found->init_fct = __libc_dlsym (found->handle, "gconv_init");
00132                 found->end_fct = __libc_dlsym (found->handle, "gconv_end");
00133 
00134 #ifdef PTR_MANGLE
00135                 PTR_MANGLE (found->fct);
00136                 if (found->init_fct != NULL)
00137                   PTR_MANGLE (found->init_fct);
00138                 if (found->end_fct !=  NULL)
00139                   PTR_MANGLE (found->end_fct);
00140 #endif
00141 
00142                 /* We have succeeded in loading the shared object.  */
00143                 found->counter = 1;
00144               }
00145            }
00146          else
00147            /* Error while loading the shared object.  */
00148            found = NULL;
00149        }
00150       else if (found->handle != NULL)
00151        found->counter = MAX (found->counter + 1, 1);
00152     }
00153 
00154   return found;
00155 }
00156 
00157 
00158 /* This is very ugly but the tsearch functions provide no way to pass
00159    information to the walker function.  So we use a global variable.
00160    It is MT safe since we use a lock.  */
00161 static struct __gconv_loaded_object *release_handle;
00162 
00163 static void
00164 do_release_shlib (void *nodep, VISIT value, int level)
00165 {
00166   struct __gconv_loaded_object *obj = *(struct __gconv_loaded_object **) nodep;
00167 
00168   if (value != preorder && value != leaf)
00169     return;
00170 
00171   if (obj == release_handle)
00172     {
00173       /* This is the object we want to unload.  Now decrement the
00174         reference counter.  */
00175       assert (obj->counter > 0);
00176       --obj->counter;
00177     }
00178   else if (obj->counter <= 0 && obj->counter >= -TRIES_BEFORE_UNLOAD
00179           && --obj->counter < -TRIES_BEFORE_UNLOAD && obj->handle != NULL)
00180     {
00181       /* Unload the shared object.  */
00182       __libc_dlclose (obj->handle);
00183       obj->handle = NULL;
00184     }
00185 }
00186 
00187 
00188 /* Notify system that a shared object is not longer needed.  */
00189 void
00190 internal_function
00191 __gconv_release_shlib (struct __gconv_loaded_object *handle)
00192 {
00193   /* Urgh, this is ugly but we have no other possibility.  */
00194   release_handle = handle;
00195 
00196   /* Process all entries.  Please note that we also visit entries
00197      with release counts <= 0.  This way we can finally unload them
00198      if necessary.  */
00199   __twalk (loaded, (__action_fn_t) do_release_shlib);
00200 }
00201 
00202 
00203 /* We run this if we debug the memory allocation.  */
00204 static void __libc_freeres_fn_section
00205 do_release_all (void *nodep)
00206 {
00207   struct __gconv_loaded_object *obj = (struct __gconv_loaded_object *) nodep;
00208 
00209   /* Unload the shared object.  */
00210   if (obj->handle != NULL)
00211     __libc_dlclose (obj->handle);
00212 
00213   free (obj);
00214 }
00215 
00216 libc_freeres_fn (free_mem)
00217 {
00218   __tdestroy (loaded, do_release_all);
00219   loaded = NULL;
00220 }
00221 
00222 
00223 #ifdef DEBUG
00224 static void
00225 do_print (const void *nodep, VISIT value, int level)
00226 {
00227   struct __gconv_loaded_object *obj = *(struct __gconv_loaded_object **) nodep;
00228 
00229   printf ("%10s: \"%s\", %d\n",
00230          value == leaf ? "leaf" :
00231          value == preorder ? "preorder" :
00232          value == postorder ? "postorder" : "endorder",
00233          obj->name, obj->counter);
00234 }
00235 
00236 static void
00237 print_all (void)
00238 {
00239   __twalk (loaded, do_print);
00240 }
00241 #endif