summary refs log tree commit diff
path: root/dlfcn
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>1999-07-07 18:39:33 +0000
committerUlrich Drepper <drepper@redhat.com>1999-07-07 18:39:33 +0000
commit94e365c61202e2472c8aea42c7c95ce40f5b843c (patch)
tree15cf6a02604ab91d5aa1feeaeb338f0fc6b91f34 /dlfcn
parent2556bfe6fdb5ccb4864e0249d5fdd475e4100c71 (diff)
downloadglibc-94e365c61202e2472c8aea42c7c95ce40f5b843c.tar.gz
glibc-94e365c61202e2472c8aea42c7c95ce40f5b843c.tar.xz
glibc-94e365c61202e2472c8aea42c7c95ce40f5b843c.zip
Update.
	* sysdeps/gnu/Makefile (libdl-routines): Add eval.

1999-07-06  Zack Weinberg  <zack@rabi.columbia.edu>

	* dlfcn: New directory.  Move the following files from elf:
	dladdr.c, dlclose.c, dlerror.c, dlfcn.h, dlopen.c,
	dlopenold.c, dlsym.c, dlvsym.c.
	* dlfcn/Makefile: New file.
	* dlfcn/Versions: New file.
	* dlfcn/dlsym.c: All ELF-specific code split out to new
	function _dl_sym.
	* dlfcn/dlvsym.c: All ELF-specific code split out to new
	function _dl_vsym.
	* elf/dl-sym.c: New file.  _dl_sym and _dl_vsym are
	implemented here.

	* dlfcn/dladdr.c: Remove all references to ELF data structures
	or headers.
	* dlfcn/dlclose.c: Likewise.
	* dlfcn/dlerror.c: Likewise.
	* dlfcn/dlopen.c: Likewise.
	* dlfcn/dlopenold.c: Likewise.

	* Makeconfig (dlfcn): New variable - set to `dlfcn' if elf is
	yes, empty otherwise.
	(libdl): Set to dlfcn/libdl.so or libdl.a if elf is yes,
	depending on build-shared.
	(subdirs): Add $(dlfcn).
	(rpath-dirs): Add dlfcn.
	* elf/Makefile: Remove all references to libdl or its
	components, except the ones relating to the test cases.
	(routines): Add dl-sym.
	* elf/Versions (libc): Add _dl_sym and _dl_vsym for GLIBC_2.2.
	(libdl): Delete.

	* elf/dl-close.c (_dl_close): Change argument to void *.
	* elf/dl-open.c (_dl_open): Change return type to void *.
	* elf/eval.c: Removed.
	* elf/ldsodefs.h: Move prototypes of _dl_catch_error,
	_dlerror_run, _dl_open, _dl_close, _dl_addr, and
	_dl_mcount_wrapper_check to include/dlfcn.h.  Delete
	_CALL_DL_FCT macro.
	* include/dlfcn.h: Also prototype _dl_sym and _dl_vsym here.
	Include real header from dlfcn directory.
	* include/ldsodefs.h: Removed.

	* grp/initgroups.c: Use DL_CALL_FCT not _CALL_DL_FCT.
	* nss/getXXbyYY_r.c: Likewise.
	* nss/getXXent_r.c: Likewise.
	* iconv/gconv.c: Likewise.  Don't include ldsodefs.h.
	* iconv/gconv_db.c: Likewise.  Don't include ldsodefs.h.
	* iconv/skeleton.c: Don't include ldsodefs.h.
	* nss/nsswitch.h: Don't include ldsodefs.h.  Include dlfcn.h.

1999-07-07  Ulrich Drepper  <drepper@cygnus.com>
Diffstat (limited to 'dlfcn')
-rw-r--r--dlfcn/Makefile34
-rw-r--r--dlfcn/Versions8
-rw-r--r--dlfcn/dladdr.c26
-rw-r--r--dlfcn/dlclose.c32
-rw-r--r--dlfcn/dlerror.c144
-rw-r--r--dlfcn/dlfcn.h100
-rw-r--r--dlfcn/dlopen.c56
-rw-r--r--dlfcn/dlopenold.c61
-rw-r--r--dlfcn/dlsym.c51
-rw-r--r--dlfcn/dlvsym.c55
10 files changed, 567 insertions, 0 deletions
diff --git a/dlfcn/Makefile b/dlfcn/Makefile
new file mode 100644
index 0000000000..ee8203cf68
--- /dev/null
+++ b/dlfcn/Makefile
@@ -0,0 +1,34 @@
+# Copyright (C) 1995, 1996, 1997, 1998, 1999 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 Library General Public License as
+# published by the Free Software Foundation; either version 2 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.
+
+# 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.
+
+subdir		:= dlfcn
+headers		:= dlfcn.h
+extra-libs	:= libdl
+libdl-routines	:= dlopen dlclose dlsym dlvsym dlerror dladdr
+distribute	:= dlopenold.c
+
+extra-libs-others := libdl
+
+include ../Makeconfig
+
+ifeq ($(versioning),yes)
+libdl-routines	+= dlopenold
+libdl-shared-only-routines := dlopenold
+endif
+
+include ../Rules
diff --git a/dlfcn/Versions b/dlfcn/Versions
new file mode 100644
index 0000000000..b99822ad08
--- /dev/null
+++ b/dlfcn/Versions
@@ -0,0 +1,8 @@
+libdl {
+  GLIBC_2.0 {
+    dladdr; dlclose; dlerror; dlopen; dlsym;
+  }
+  GLIBC_2.1 {
+    dlopen; dlvsym;
+  }
+}
diff --git a/dlfcn/dladdr.c b/dlfcn/dladdr.c
new file mode 100644
index 0000000000..2bcc25e2fd
--- /dev/null
+++ b/dlfcn/dladdr.c
@@ -0,0 +1,26 @@
+/* Locate the shared object symbol nearest a given address.
+   Copyright (C) 1996, 1997, 1998, 1999 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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.
+
+   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.  */
+
+#include <dlfcn.h>
+
+int
+dladdr (const void *address, Dl_info *info)
+{
+  return _dl_addr (address, info);
+}
diff --git a/dlfcn/dlclose.c b/dlfcn/dlclose.c
new file mode 100644
index 0000000000..dea2391128
--- /dev/null
+++ b/dlfcn/dlclose.c
@@ -0,0 +1,32 @@
+/* Close a handle opened by `dlopen'.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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.
+
+   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.  */
+
+#include <dlfcn.h>
+
+static void
+dlclose_doit (void *handle)
+{
+  _dl_close (handle);
+}
+
+int
+dlclose (void *handle)
+{
+  return _dlerror_run (dlclose_doit, handle) ? -1 : 0;
+}
diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c
new file mode 100644
index 0000000000..c43d511156
--- /dev/null
+++ b/dlfcn/dlerror.c
@@ -0,0 +1,144 @@
+/* Return error detail for failing <dlfcn.h> functions.
+   Copyright (C) 1995, 1996, 1997, 1998 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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.
+
+   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.  */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <bits/libc-lock.h>
+
+/* Type for storing results of dynamic loading actions.  */
+struct dl_action_result
+  {
+    int errcode;
+    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;
+
+/* Destructor for the thread-specific data.  */
+static void init (void);
+static void free_key_mem (void *mem);
+
+
+char *
+dlerror (void)
+{
+  static char *buf;
+  struct dl_action_result *result;
+
+  if (buf)
+    {
+      free (buf);
+      buf = NULL;
+    }
+
+  /* Get error string.  */
+  result = (struct dl_action_result *) __libc_getspecific (key);
+  if (result == NULL)
+    result = &last_result;
+
+  if (! result->errstring)
+    return NULL;
+
+  if (result->errcode == 0)
+    buf = result->errstring;
+  else
+    {
+      if (__asprintf (&buf, "%s: %s",
+		      result->errstring, strerror (result->errcode)) == -1)
+	buf = NULL;
+
+      /* We don't need the error string anymore.  */
+      free (result->errstring);
+    }
+
+  /* Reset the error indicator.  */
+  result->errstring = NULL;
+
+  return buf;
+}
+
+int
+internal_function
+_dlerror_run (void (*operate) (void *), void *args)
+{
+  __libc_once_define (static, once);
+  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.  */
+    free (result->errstring);
+
+  result->errcode = _dl_catch_error (&result->errstring, operate, args);
+
+  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;
+}
+
+
+/* Free the thread specific data, this is done if a thread terminates.  */
+static void
+free_key_mem (void *mem)
+{
+  free (mem);
+  __libc_setspecific (key, NULL);
+}
diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h
new file mode 100644
index 0000000000..76fdc3c1c9
--- /dev/null
+++ b/dlfcn/dlfcn.h
@@ -0,0 +1,100 @@
+/* User functions for run-time dynamic loading.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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.
+
+   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.  */
+
+#ifndef	_DLFCN_H
+#define	_DLFCN_H 1
+
+#include <features.h>
+#define __need_NULL
+#include <stddef.h>
+
+/* Collect various system dependent definitions and declarations.  */
+#include <bits/dlfcn.h>
+
+/* If the first argument of `dlsym' or `dlvsym' is set to RTLD_NEXT
+   the run-time address of the symbol called NAME in the next shared
+   object is returned.  The "next" relation is defined by the order
+   the shared objects were loaded.  */
+#define RTLD_NEXT	((void *) -1l)
+
+/* If the first argument to `dlsym' or `dlvsym' is set to RTLD_DEFAULT
+   the run-time address of the symbol called NAME in the global scope
+   is returned.  */
+#define RTLD_DEFAULT	NULL
+
+__BEGIN_DECLS
+
+/* Open the shared object FILE and map it in; return a handle that can be
+   passed to `dlsym' to get symbol values from it.  */
+extern void *dlopen __P ((__const char *__file, int __mode));
+
+/* Unmap and close a shared object opened by `dlopen'.
+   The handle cannot be used again after calling `dlclose'.  */
+extern int dlclose __P ((void *__handle));
+
+/* Find the run-time address in the shared object HANDLE refers to
+   of the symbol called NAME.  */
+extern void *dlsym __P ((void *__restrict __handle,
+			 __const char *__restrict __name));
+
+#ifdef __USE_GNU
+/* Find the run-time address in the shared object HANDLE refers to
+   of the symbol called NAME with VERSION.  */
+extern void *dlvsym __P ((void *__restrict __handle,
+			  __const char *__restrict __name,
+			  __const char *__restrict __version));
+#endif
+
+/* When any of the above functions fails, call this function
+   to return a string describing the error.  Each call resets
+   the error string so that a following call returns null.  */
+extern char *dlerror __P ((void));
+
+#ifdef __USE_GNU
+/* Fill in *INFO with the following information about ADDRESS.
+   Returns 0 iff no shared object's segments contain that address.  */
+typedef struct
+  {
+    __const char *dli_fname;	/* File name of defining object.  */
+    void *dli_fbase;		/* Load address of that object.  */
+    __const char *dli_sname;	/* Name of nearest symbol.  */
+    void *dli_saddr;		/* Exact value of nearest symbol.  */
+  } Dl_info;
+extern int dladdr __P ((const void *__address, Dl_info *__info));
+
+/* To support profiling of shared objects it is a good idea to call
+   the function found using `dlsym' using the following macro since
+   these calls do not use the PLT.  But this would mean the dynamic
+   loader has no chance to find out when the function is called.  The
+   macro applies the necessary magic so that profiling is possible.
+   Rewrite
+	foo = (*fctp) (arg1, arg2);
+   into
+        foo = DL_CALL_FCT (fctp, (arg1, arg2));
+*/
+# define DL_CALL_FCT(fctp, args) \
+  (_dl_mcount_wrapper_check (fctp), (*(fctp)) args)
+
+/* This function calls the profiling functions.  */
+extern void _dl_mcount_wrapper_check __P ((void *__selfpc));
+#endif
+
+__END_DECLS
+
+#endif	/* dlfcn.h */
diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c
new file mode 100644
index 0000000000..cb49e6b142
--- /dev/null
+++ b/dlfcn/dlopen.c
@@ -0,0 +1,56 @@
+/* Load a shared object at run time.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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.
+
+   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.  */
+
+#include <dlfcn.h>
+
+struct dlopen_args
+{
+  /* The arguments for dlopen_doit.  */
+  const char *file;
+  int mode;
+  /* The return value of dlopen_doit.  */
+  void *new;
+  /* Address of the caller.  */
+  const void *caller;
+};
+
+static void
+dlopen_doit (void *a)
+{
+  struct dlopen_args *args = (struct dlopen_args *) a;
+
+  args->new = _dl_open (args->file ?: "", args->mode, args->caller);
+}
+
+
+void *
+__dlopen_check (const char *file, int mode)
+{
+  struct dlopen_args args;
+  args.file = file;
+  args.mode = mode;
+  args.caller = __builtin_return_address (0);
+
+  return _dlerror_run (dlopen_doit, &args) ? NULL : args.new;
+}
+#if defined PIC && defined DO_VERSIONING
+default_symbol_version (__dlopen_check, dlopen, GLIBC_2.1);
+#else
+weak_alias (__dlopen_check, dlopen)
+#endif
diff --git a/dlfcn/dlopenold.c b/dlfcn/dlopenold.c
new file mode 100644
index 0000000000..f453cfb881
--- /dev/null
+++ b/dlfcn/dlopenold.c
@@ -0,0 +1,61 @@
+/* Load a shared object at run time.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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.
+
+   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.  */
+
+#include <dlfcn.h>
+
+/* This file is for compatibility with glibc 2.0.  Compile it only if
+   versioning is used.  */
+#if defined PIC && DO_VERSIONING
+
+struct dlopen_args
+{
+  /* The arguments for dlopen_doit.  */
+  const char *file;
+  int mode;
+  /* The return value of dlopen_doit.  */
+  void *new;
+  /* Address of the caller.  */
+  const void *caller;
+};
+
+
+static void
+dlopen_doit (void *a)
+{
+  struct dlopen_args *args = (struct dlopen_args *) a;
+
+  args->new = _dl_open (args->file ?: "", args->mode, args->caller);
+}
+
+void *
+__dlopen_nocheck (const char *file, int mode)
+{
+  struct dlopen_args args;
+  args.file = file;
+  args.caller = __builtin_return_address (0);
+
+  if ((mode & RTLD_BINDING_MASK) == 0)
+    /* By default assume RTLD_LAZY.  */
+    mode |= RTLD_LAZY;
+  args.mode = mode;
+
+  return _dlerror_run (dlopen_doit, &args) ? NULL : args.new;
+}
+symbol_version (__dlopen_nocheck, dlopen, GLIBC_2.0);
+#endif
diff --git a/dlfcn/dlsym.c b/dlfcn/dlsym.c
new file mode 100644
index 0000000000..9a8fcdfbe6
--- /dev/null
+++ b/dlfcn/dlsym.c
@@ -0,0 +1,51 @@
+/* Look up a symbol in a shared object loaded by `dlopen'.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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.
+
+   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.  */
+
+#include <dlfcn.h>
+
+struct dlsym_args
+{
+  /* The arguments to dlsym_doit.  */
+  void *handle;
+  const char *name;
+  void *who;
+
+  /* The return value of dlsym_doit.  */
+  void *sym;
+};
+
+static void
+dlsym_doit (void *a)
+{
+  struct dlsym_args *args = (struct dlsym_args *) a;
+
+  args->sym = _dl_sym (args->handle, args->name, args->who);
+}
+
+
+void *
+dlsym (void *handle, const char *name)
+{
+  struct dlsym_args args;
+  args.who = __builtin_return_address (0);
+  args.handle = handle;
+  args.name = name;
+
+  return (_dlerror_run (dlsym_doit, &args) ? NULL : args.sym);
+}
diff --git a/dlfcn/dlvsym.c b/dlfcn/dlvsym.c
new file mode 100644
index 0000000000..a279b95ae9
--- /dev/null
+++ b/dlfcn/dlvsym.c
@@ -0,0 +1,55 @@
+/* Look up a versioned symbol in a shared object loaded by `dlopen'.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999 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 Library General Public License as
+   published by the Free Software Foundation; either version 2 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.
+
+   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.  */
+
+#include <dlfcn.h>
+
+struct dlvsym_args
+{
+  /* The arguments to dlvsym_doit.  */
+  void *handle;
+  const char *name;
+  const char *version;
+  void *who;
+
+  /* The return values of dlvsym_doit.  */
+  void *sym;
+};
+
+
+static void
+dlvsym_doit (void *a)
+{
+  struct dlvsym_args *args = (struct dlvsym_args *)a;
+
+  args->sym = _dl_vsym (args->handle, args->name, args->version, args->who);
+}
+
+void *
+__dlvsym (void *handle, const char *name, const char *version_str)
+{
+  struct dlvsym_args args;
+
+  args.handle = handle;
+  args.name = name;
+  args.who = __builtin_return_address (0);
+  args.version = version_str;
+
+  return (_dlerror_run (dlvsym_doit, &args) ? NULL : args.sym);
+}
+weak_alias (__dlvsym, dlvsym)