(__gconv_read_conf): Don't call realpath.
[kopensolaris-gnu/glibc.git] / iconv / gconv_dl.c
index 8375040..d07f84e 100644 (file)
@@ -1,5 +1,5 @@
 /* Handle loading/unloading of shared object for transformation.
-   Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
 
    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>
 
 
+#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
    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);
+  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 result;
+  return strcmp (s1->name, s2->name);
 }
 
-
-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
-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);
-}
-
-
-void *
-internal_function
-__gconv_find_func (void *handle, const char *name)
-{
-  struct get_sym_args args;
-
-  args.map = handle;
-  args.name = name;
-
-  return (dlerror_run (get_sym, &args) ? NULL
-         : (void *) (args.loadbase + args.ref->st_value));
-}
-
-
-
 /* 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
@@ -139,11 +79,11 @@ __gconv_find_shlib (const char *name)
      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));
+      found = malloc (sizeof (struct __gconv_loaded_object));
       if (found != NULL)
        {
          /* Point the tree node at this new structure.  */
@@ -151,7 +91,8 @@ __gconv_find_shlib (const char *name)
          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);
@@ -159,6 +100,8 @@ __gconv_find_shlib (const char *name)
            }
        }
     }
+  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
@@ -167,9 +110,10 @@ __gconv_find_shlib (const char *name)
     {
       if (found->counter < -TRIES_BEFORE_UNLOAD)
        {
-         if (dlerror_run (do_open, found) == 0)
+         found->handle = __libc_dlopen (found->name);
+         if (found->handle != NULL)
            {
-             found->fct = __gconv_find_func (found->handle, "gconv");
+             found->fct = __libc_dlsym (found->handle, "gconv");
              if (found->fct == NULL)
                {
                  /* Argh, no conversion function.  There is something
@@ -179,10 +123,8 @@ __gconv_find_shlib (const char *name)
                }
              else
                {
-                 found->init_fct = __gconv_find_func (found->handle,
-                                                      "gconv_init");
-                 found->end_fct = __gconv_find_func (found->handle,
-                                                     "gconv_end");
+                 found->init_fct = __libc_dlsym (found->handle, "gconv_init");
+                 found->end_fct = __libc_dlsym (found->handle, "gconv_end");
 
                  /* We have succeeded in loading the shared object.  */
                  found->counter = 1;
@@ -203,29 +145,29 @@ __gconv_find_shlib (const char *name)
 /* 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)
 {
-  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;
+    {
+      /* This is the object we want to unload.  Now decrement the
+        reference counter.  */
+      assert (obj->counter > 0);
+      --obj->counter;
+    }
   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);
-
+         /* Unload the shared object.  */
+         __libc_dlclose (obj->handle);
          obj->handle = NULL;
        }
     }
@@ -235,7 +177,7 @@ 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 (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;
@@ -245,5 +187,47 @@ __gconv_release_shlib (struct gconv_loaded_object *handle)
      if necessary.  */
   __twalk (loaded, do_release_shlib);
 
-  return GCONV_OK;
+  return __GCONV_OK;
+}
+
+
+/* We run this if we debug the memory allocation.  */
+static void
+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);
+}
+
+static void __attribute__ ((unused))
+free_mem (void)
+{
+  __tdestroy (loaded, do_release_all);
+}
+text_set_element (__libc_subfreeres, free_mem);
+
+
+#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