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