aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'iconv/gconv_dl.c')
-rw-r--r--iconv/gconv_dl.c85
1 files changed, 37 insertions, 48 deletions
diff --git a/iconv/gconv_dl.c b/iconv/gconv_dl.c
index 2a7cc92a3d..b11e156a03 100644
--- a/iconv/gconv_dl.c
+++ b/iconv/gconv_dl.c
@@ -40,27 +40,9 @@
#define TRIES_BEFORE_UNLOAD 2
-/* Structure describing one loaded shared object. This normally are
- objects to perform conversation but as a special case the db shared
- object is also handled. */
-struct loaded_object
-{
- /* Name of the object. */
- const char *name;
-
- /* Reference counter for the db functionality. If no conversion is
- needed we unload the db library. */
- int counter;
-
- /* The handle for the shared object. */
- void *handle;
-};
-
-
/* Array of loaded objects. This is shared by all threads so we have
to use semaphores to access it. */
static void *loaded;
-__libc_lock_define_initialized (static, lock)
@@ -68,8 +50,10 @@ __libc_lock_define_initialized (static, lock)
static int
known_compare (const void *p1, const void *p2)
{
- const struct loaded_object *s1 = (const struct loaded_object *) p1;
- const struct loaded_object *s2 = (const struct loaded_object *) 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;
}
@@ -78,7 +62,7 @@ known_compare (const void *p1, const void *p2)
static void
do_open (void *a)
{
- struct loaded_object *args = (struct loaded_object *) 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);
}
@@ -124,9 +108,9 @@ get_sym (void *a)
}
-void *
+static void *
internal_function
-__gconv_find_func (void *handle, const char *name)
+find_func (void *handle, const char *name)
{
struct get_sym_args args;
@@ -141,15 +125,11 @@ __gconv_find_func (void *handle, const char *name)
/* Open the gconv database if necessary. A non-negative return value
means success. */
-void *
+struct gconv_loaded_object *
internal_function
__gconv_find_shlib (const char *name)
{
- void *result = NULL;
- struct loaded_object *found;
-
- /* Acquire the lock. */
- __libc_lock_lock (lock);
+ struct gconv_loaded_object *found;
/* Search the tree of shared objects previously requested. Data in
the tree are `loaded_object' structures, whose first member is a
@@ -164,7 +144,7 @@ __gconv_find_shlib (const char *name)
if (found == NULL)
{
/* This name was not known before. */
- found = malloc (sizeof (struct loaded_object));
+ found = malloc (sizeof (struct gconv_loaded_object));
if (found != NULL)
{
/* Point the tree node at this new structure. */
@@ -189,35 +169,50 @@ __gconv_find_shlib (const char *name)
if (found->counter < -TRIES_BEFORE_UNLOAD)
{
if (dlerror_run (do_open, found) == 0)
- found->counter = 1;
+ {
+ found->fct = find_func (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 = find_func (found->handle, "gconv_init");
+ found->end_fct = find_func (found->handle, "gconv_end");
+
+ /* 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);
-
- result = found->handle;
}
- /* Release the lock. */
- __libc_lock_unlock (lock);
-
- return result;
+ 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 void *release_handle;
+static struct gconv_loaded_object *release_handle;
static void
do_release_shlib (const void *nodep, VISIT value, int level)
{
- struct loaded_object *obj = *(struct loaded_object **) nodep;
+ struct gconv_loaded_object *obj = *(struct gconv_loaded_object **) nodep;
if (value != preorder && value != leaf)
return;
- if (obj->handle == release_handle)
+ if (obj == release_handle)
/* This is the object we want to unload. Now set the release
counter to zero. */
obj->counter = 0;
@@ -228,7 +223,7 @@ do_release_shlib (const void *nodep, VISIT value, int level)
/* 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 ((struct link_map *) obj->handle);
+ _dl_close (obj->handle);
obj->handle = NULL;
}
@@ -239,11 +234,8 @@ do_release_shlib (const void *nodep, VISIT value, int level)
/* Notify system that a shared object is not longer needed. */
int
internal_function
-__gconv_release_shlib (void *handle)
+__gconv_release_shlib (struct gconv_loaded_object *handle)
{
- /* Acquire the lock. */
- __libc_lock_lock (lock);
-
/* Urgh, this is ugly but we have no other possibility. */
release_handle = handle;
@@ -252,8 +244,5 @@ __gconv_release_shlib (void *handle)
if necessary. */
__twalk (loaded, do_release_shlib);
- /* Release the lock. */
- __libc_lock_unlock (lock);
-
return GCONV_OK;
}