]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - iconv/gconv_dl.c
Update copyright dates with scripts/update-copyrights.
[thirdparty/glibc.git] / iconv / gconv_dl.c
index b11e156a037ad2e06c955c4a536b16e5c380fea3..0ed71b74985da665e8d1e70e2222d113e7391500 100644 (file)
@@ -1,33 +1,39 @@
 /* Handle loading/unloading of shared object for transformation.
-   Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1997-2019 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, 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 <bits/libc-lock.h>
-#include <elf/ldsodefs.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
    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 *
-internal_function
+struct __gconv_loaded_object *
 __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
@@ -140,19 +78,22 @@ __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));
+      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);
@@ -160,6 +101,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
@@ -168,9 +111,11 @@ __gconv_find_shlib (const char *name)
     {
       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
@@ -180,8 +125,14 @@ __gconv_find_shlib (const char *name)
                }
              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);
+                 PTR_MANGLE (found->init_fct);
+                 PTR_MANGLE (found->end_fct);
+#endif
 
                  /* We have succeeded in loading the shared object.  */
                  found->counter = 1;
@@ -202,39 +153,36 @@ __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)
+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
-internal_function
-__gconv_release_shlib (struct gconv_loaded_object *handle)
+void
+__gconv_release_shlib (struct __gconv_loaded_object *handle)
 {
   /* Urgh, this is ugly but we have no other possibility.  */
   release_handle = handle;
@@ -242,7 +190,49 @@ __gconv_release_shlib (struct gconv_loaded_object *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);
+}
+
+
+/* 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;
 
-  return GCONV_OK;
+  /* 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