/* Handle loading/unloading of shared object for transformation.
- Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005
+ 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 Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
+ 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
- Library General Public License for more details.
+ Lesser General Public License for more details.
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+#include <assert.h>
#include <dlfcn.h>
#include <inttypes.h>
#include <search.h>
#include <stdlib.h>
#include <string.h>
#include <bits/libc-lock.h>
-#include <elf/ldsodefs.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
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 (intptr_t) s1->handle - (intptr_t) s2->handle;
-}
-
-
-static void
-do_open (void *a)
-{
- struct gconv_loaded_object *args = (struct gconv_loaded_object *) a;
- /* Open and relocate the shared object. */
- args->handle = _dl_open (args->name, RTLD_LAZY);
-}
-
-
-static int
-internal_function
-dlerror_run (void (*operate) (void *), void *args)
-{
- char *last_errstring = NULL;
- int result;
-
- (void) _dl_catch_error (&last_errstring, operate, args);
-
- result = last_errstring != NULL;
- if (result)
- free (last_errstring);
-
- return result;
-}
-
-
-struct get_sym_args
-{
- /* Arguments to get_sym. */
- struct link_map *map;
- const char *name;
-
- /* Return values of get_sym. */
- ElfW(Addr) loadbase;
- const ElfW(Sym) *ref;
-};
-
-static void
-internal_function
-get_sym (void *a)
-{
- struct get_sym_args *args = (struct get_sym_args *) a;
- struct link_map *scope[2] = { args->map, NULL };
- args->ref = NULL;
- args->loadbase = _dl_lookup_symbol (args->name, &args->ref,
- scope, args->map->l_name, 0);
-}
-
-
-static void *
-internal_function
-find_func (void *handle, const char *name)
-{
- struct get_sym_args args;
-
- args.map = handle;
- args.name = name;
+ 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 (dlerror_run (get_sym, &args) ? NULL
- : (void *) (args.loadbase + args.ref->st_value));
+ return strcmp (s1->name, s2->name);
}
-
-
/* Open the gconv database if necessary. A non-negative return value
means success. */
-struct gconv_loaded_object *
+struct __gconv_loaded_object *
internal_function
__gconv_find_shlib (const char *name)
{
- struct gconv_loaded_object *found;
+ 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
enough to a pointer to our structure to use as a lookup key that
will be passed to `known_compare' (above). */
- found = __tfind (&name, &loaded, known_compare);
- if (found == NULL)
+ keyp = __tfind (&name, &loaded, known_compare);
+ if (keyp == NULL)
{
/* This name was not known before. */
- found = malloc (sizeof (struct gconv_loaded_object));
+ 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 = name;
+ found->name = (char *) memcpy (found + 1, name, namelen);
found->counter = -TRIES_BEFORE_UNLOAD - 1;
found->handle = NULL;
- if (__tsearch (found, &loaded, known_compare) == NULL)
+ if (__builtin_expect (__tsearch (found, &loaded, known_compare)
+ == NULL, 0))
{
/* Something went wrong while inserting the entry. */
free (found);
}
}
}
+ 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
{
if (found->counter < -TRIES_BEFORE_UNLOAD)
{
- if (dlerror_run (do_open, found) == 0)
+ assert (found->handle == NULL);
+ found->handle = __libc_dlopen (found->name);
+ if (found->handle != NULL)
{
- found->fct = find_func (found->handle, "gconv");
+ found->fct = __libc_dlsym (found->handle, "gconv");
if (found->fct == NULL)
{
/* Argh, no conversion function. There is something
}
else
{
- found->init_fct = find_func (found->handle, "gconv_init");
- found->end_fct = find_func (found->handle, "gconv_end");
+ 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;
/* 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 struct __gconv_loaded_object *release_handle;
static void
-do_release_shlib (const void *nodep, VISIT value, int level)
+do_release_shlib (void *nodep, VISIT value, int level)
{
- struct gconv_loaded_object *obj = *(struct gconv_loaded_object **) nodep;
+ 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 set the release
- counter to zero. */
- obj->counter = 0;
- else if (obj->counter <= 0)
{
- if (--obj->counter < -TRIES_BEFORE_UNLOAD && obj->handle != NULL)
- {
- /* Unload the shared object. We don't use the trick to
- catch errors since in the case an error is signalled
- something is really wrong. */
- _dl_close (obj->handle);
-
- obj->handle = NULL;
- }
+ /* 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. */
-int
+void
internal_function
-__gconv_release_shlib (struct gconv_loaded_object *handle)
+__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, do_release_shlib);
+ __twalk (loaded, (__action_fn_t) do_release_shlib);
+}
- return GCONV_OK;
+
+/* 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
+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
+print_all (void)
+{
+ __twalk (loaded, do_print);
}
+#endif