about summary refs log tree commit diff
path: root/REORG.TODO/dlfcn/dlerror.c
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/dlfcn/dlerror.c')
-rw-r--r--REORG.TODO/dlfcn/dlerror.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/REORG.TODO/dlfcn/dlerror.c b/REORG.TODO/dlfcn/dlerror.c
new file mode 100644
index 0000000000..c0ead7dcb6
--- /dev/null
+++ b/REORG.TODO/dlfcn/dlerror.c
@@ -0,0 +1,254 @@
+/* Return error detail for failing <dlfcn.h> functions.
+   Copyright (C) 1995-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   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 <dlfcn.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libc-lock.h>
+#include <ldsodefs.h>
+
+#if !defined SHARED && IS_IN (libdl)
+
+char *
+dlerror (void)
+{
+  return __dlerror ();
+}
+
+#else
+
+/* Type for storing results of dynamic loading actions.  */
+struct dl_action_result
+  {
+    int errcode;
+    int returned;
+    bool malloced;
+    const char *objname;
+    const char *errstring;
+  };
+static struct dl_action_result last_result;
+static struct dl_action_result *static_buf;
+
+/* This is the key for the thread specific memory.  */
+static __libc_key_t key;
+__libc_once_define (static, once);
+
+/* Destructor for the thread-specific data.  */
+static void init (void);
+static void free_key_mem (void *mem);
+
+
+char *
+__dlerror (void)
+{
+  char *buf = NULL;
+  struct dl_action_result *result;
+
+# ifdef SHARED
+  if (__glibc_unlikely (_dlfcn_hook != NULL))
+    return _dlfcn_hook->dlerror ();
+# endif
+
+  /* If we have not yet initialized the buffer do it now.  */
+  __libc_once (once, init);
+
+  /* Get error string.  */
+  result = (struct dl_action_result *) __libc_getspecific (key);
+  if (result == NULL)
+    result = &last_result;
+
+  /* Test whether we already returned the string.  */
+  if (result->returned != 0)
+    {
+      /* We can now free the string.  */
+      if (result->errstring != NULL)
+	{
+	  if (strcmp (result->errstring, "out of memory") != 0)
+	    free ((char *) result->errstring);
+	  result->errstring = NULL;
+	}
+    }
+  else if (result->errstring != NULL)
+    {
+      buf = (char *) result->errstring;
+      int n;
+      if (result->errcode == 0)
+	n = __asprintf (&buf, "%s%s%s",
+			result->objname,
+			result->objname[0] == '\0' ? "" : ": ",
+			_(result->errstring));
+      else
+	n = __asprintf (&buf, "%s%s%s: %s",
+			result->objname,
+			result->objname[0] == '\0' ? "" : ": ",
+			_(result->errstring),
+			strerror (result->errcode));
+      if (n != -1)
+	{
+	  /* We don't need the error string anymore.  */
+	  if (strcmp (result->errstring, "out of memory") != 0)
+	    free ((char *) result->errstring);
+	  result->errstring = buf;
+	}
+
+      /* Mark the error as returned.  */
+      result->returned = 1;
+    }
+
+  return buf;
+}
+# ifdef SHARED
+strong_alias (__dlerror, dlerror)
+# endif
+
+int
+internal_function
+_dlerror_run (void (*operate) (void *), void *args)
+{
+  struct dl_action_result *result;
+
+  /* If we have not yet initialized the buffer do it now.  */
+  __libc_once (once, init);
+
+  /* Get error string and number.  */
+  if (static_buf != NULL)
+    result = static_buf;
+  else
+    {
+      /* We don't use the static buffer and so we have a key.  Use it
+	 to get the thread-specific buffer.  */
+      result = __libc_getspecific (key);
+      if (result == NULL)
+	{
+	  result = (struct dl_action_result *) calloc (1, sizeof (*result));
+	  if (result == NULL)
+	    /* We are out of memory.  Since this is no really critical
+	       situation we carry on by using the global variable.
+	       This might lead to conflicts between the threads but
+	       they soon all will have memory problems.  */
+	    result = &last_result;
+	  else
+	    /* Set the tsd.  */
+	    __libc_setspecific (key, result);
+	}
+    }
+
+  if (result->errstring != NULL)
+    {
+      /* Free the error string from the last failed command.  This can
+	 happen if `dlerror' was not run after an error was found.  */
+      if (result->malloced)
+	free ((char *) result->errstring);
+      result->errstring = NULL;
+    }
+
+  result->errcode = _dl_catch_error (&result->objname, &result->errstring,
+				     &result->malloced, operate, args);
+
+  /* If no error we mark that no error string is available.  */
+  result->returned = result->errstring == NULL;
+
+  return result->errstring != NULL;
+}
+
+
+/* Initialize buffers for results.  */
+static void
+init (void)
+{
+  if (__libc_key_create (&key, free_key_mem))
+    /* Creating the key failed.  This means something really went
+       wrong.  In any case use a static buffer which is better than
+       nothing.  */
+    static_buf = &last_result;
+}
+
+
+static void
+check_free (struct dl_action_result *rec)
+{
+  if (rec->errstring != NULL
+      && strcmp (rec->errstring, "out of memory") != 0)
+    {
+      /* We can free the string only if the allocation happened in the
+	 C library used by the dynamic linker.  This means, it is
+	 always the C library in the base namespace.  When we're statically
+         linked, the dynamic linker is part of the program and so always
+	 uses the same C library we use here.  */
+#ifdef SHARED
+      struct link_map *map = NULL;
+      Dl_info info;
+      if (_dl_addr (check_free, &info, &map, NULL) != 0 && map->l_ns == 0)
+#endif
+	free ((char *) rec->errstring);
+    }
+}
+
+
+static void
+__attribute__ ((destructor))
+fini (void)
+{
+  check_free (&last_result);
+}
+
+
+/* Free the thread specific data, this is done if a thread terminates.  */
+static void
+free_key_mem (void *mem)
+{
+  check_free ((struct dl_action_result *) mem);
+
+  free (mem);
+  __libc_setspecific (key, NULL);
+}
+
+# ifdef SHARED
+
+struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
+libdl_hidden_data_def (_dlfcn_hook)
+
+# else
+
+static struct dlfcn_hook _dlfcn_hooks =
+  {
+    .dlopen = __dlopen,
+    .dlclose = __dlclose,
+    .dlsym = __dlsym,
+    .dlvsym = __dlvsym,
+    .dlerror = __dlerror,
+    .dladdr = __dladdr,
+    .dladdr1 = __dladdr1,
+    .dlinfo = __dlinfo,
+    .dlmopen = __dlmopen
+  };
+
+void
+__libc_register_dlfcn_hook (struct link_map *map)
+{
+  struct dlfcn_hook **hook;
+
+  hook = (struct dlfcn_hook **) __libc_dlsym_private (map, "_dlfcn_hook");
+  if (hook != NULL)
+    *hook = &_dlfcn_hooks;
+}
+# endif
+#endif