summary refs log tree commit diff
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2004-10-18 23:17:46 +0000
committerUlrich Drepper <drepper@redhat.com>2004-10-18 23:17:46 +0000
commit5f21997b9d6a49ddbed85b044e2be7b182c095a2 (patch)
tree3b63a3a63ebeb466456f52480bd06df198f1dcf5
parent790b6c7a7f65dec508a2e71e6d922ec166640afc (diff)
downloadglibc-5f21997b9d6a49ddbed85b044e2be7b182c095a2.tar.gz
glibc-5f21997b9d6a49ddbed85b044e2be7b182c095a2.tar.xz
glibc-5f21997b9d6a49ddbed85b044e2be7b182c095a2.zip
2004-10-18  Jakub Jelinek  <jakub@redhat.com>

	* elf/dl-libc.c (__libc_dlsym_private, __libc_register_dl_open_hook):
	New functions.
	(__libc_dlopen_mode): Call __libc_register_dl_open_hook and
	__libc_register_dlfcn_hook.
	* dlfcn/Makefile (routines, elide-routines.os): Set.
	Add rules to build and test tststatic2.
	* dlfcn/tststatic2.c: New test.
	* dlfcn/modstatic2.c: New test module.
	* dlfcn/dladdr.c: Call _dlfcn_hook from libdl.so if not NULL.
	Define __ prefixed routine in libc.a and in libdl.a just call it.
	* dlfcn/dladdr1.c: Likewise.
	* dlfcn/dlclose.c: Likewise.
	* dlfcn/dlerror.c: Likewise.
	* dlfcn/dlinfo.c: Likewise.
	* dlfcn/dlmopen.c: Likewise.
	* dlfcn/dlopen.c: Likewise.
	* dlfcn/dlopenold.c: Likewise.
	* dlfcn/dlsym.c: Likewise.
	* dlfcn/dlvsym.c: Likewise.
	* dlfcn/sdladdr.c: New file.
	* dlfcn/sdladdr1.c: New file.
	* dlfcn/sdlclose.c: New file.
	* dlfcn/sdlerror.c: New file.
	* dlfcn/sdlinfo.c: New file.
	* dlfcn/sdlopen.c: New file.
	* dlfcn/sdlsym.c: New file.
	* dlfcn/sdlvsym.c: New file.
	* dlfcn/Versions (libdl): Export _dlfcn_hook@GLIBC_PRIVATE.
	* include/dlfcn.h (DL_CALLER_DECL, DL_CALLER RETURN_ADDRESS): Define.
	(struct dlfcn_hook): New type.
	(_dlfcn_hook): New extern decl.
	(__dlopen, __dlclose, __dlsym, __dlerror, __dladdr, __dladdr1,
	__dlinfo, __dlmopen, __libc_dlsym_private,
	__libc_register_dl_open_hook, __libc_register_dlfcn_hook): New
	prototypes.
	(__dlvsym): Use DL_CALLER_DECL.
	* include/libc-symbols.h: Define libdl_hidden_proto and friends.

	* malloc/arena.c (_dl_open_hook): Extern decl.
	(ptmalloc_init): Don't call _dl_addr when dlopened from statically
	linked programs but don't use brk for them either.
-rw-r--r--ChangeLog44
-rw-r--r--dlfcn/Makefile18
-rw-r--r--dlfcn/Versions3
-rw-r--r--dlfcn/dladdr.c21
-rw-r--r--dlfcn/dladdr1.c21
-rw-r--r--dlfcn/dlclose.c24
-rw-r--r--dlfcn/dlerror.c52
-rw-r--r--dlfcn/dlinfo.c28
-rw-r--r--dlfcn/dlmopen.c39
-rw-r--r--dlfcn/dlopen.c48
-rw-r--r--dlfcn/dlopenold.c3
-rw-r--r--dlfcn/dlsym.c23
-rw-r--r--dlfcn/dlvsym.c25
-rw-r--r--dlfcn/modstatic2.c228
-rw-r--r--dlfcn/sdladdr.c1
-rw-r--r--dlfcn/sdladdr1.c1
-rw-r--r--dlfcn/sdlclose.c1
-rw-r--r--dlfcn/sdlerror.c1
-rw-r--r--dlfcn/sdlinfo.c1
-rw-r--r--dlfcn/sdlmopen.c1
-rw-r--r--dlfcn/sdlopen.c1
-rw-r--r--dlfcn/sdlsym.c1
-rw-r--r--dlfcn/sdlvsym.c1
-rw-r--r--dlfcn/tststatic2.c166
-rw-r--r--include/dlfcn.h60
-rw-r--r--include/libc-symbols.h18
-rw-r--r--malloc/arena.c11
27 files changed, 803 insertions, 38 deletions
diff --git a/ChangeLog b/ChangeLog
index 1a056e6685..0e173987cc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,47 @@
+2004-10-18  Jakub Jelinek  <jakub@redhat.com>
+
+	* elf/dl-libc.c (__libc_dlsym_private, __libc_register_dl_open_hook):
+	New functions.
+	(__libc_dlopen_mode): Call __libc_register_dl_open_hook and
+	__libc_register_dlfcn_hook.
+	* dlfcn/Makefile (routines, elide-routines.os): Set.
+	Add rules to build and test tststatic2.
+	* dlfcn/tststatic2.c: New test.
+	* dlfcn/modstatic2.c: New test module.
+	* dlfcn/dladdr.c: Call _dlfcn_hook from libdl.so if not NULL.
+	Define __ prefixed routine in libc.a and in libdl.a just call it.
+	* dlfcn/dladdr1.c: Likewise.
+	* dlfcn/dlclose.c: Likewise.
+	* dlfcn/dlerror.c: Likewise.
+	* dlfcn/dlinfo.c: Likewise.
+	* dlfcn/dlmopen.c: Likewise.
+	* dlfcn/dlopen.c: Likewise.
+	* dlfcn/dlopenold.c: Likewise.
+	* dlfcn/dlsym.c: Likewise.
+	* dlfcn/dlvsym.c: Likewise.
+	* dlfcn/sdladdr.c: New file.
+	* dlfcn/sdladdr1.c: New file.
+	* dlfcn/sdlclose.c: New file.
+	* dlfcn/sdlerror.c: New file.
+	* dlfcn/sdlinfo.c: New file.
+	* dlfcn/sdlopen.c: New file.
+	* dlfcn/sdlsym.c: New file.
+	* dlfcn/sdlvsym.c: New file.
+	* dlfcn/Versions (libdl): Export _dlfcn_hook@GLIBC_PRIVATE.
+	* include/dlfcn.h (DL_CALLER_DECL, DL_CALLER RETURN_ADDRESS): Define.
+	(struct dlfcn_hook): New type.
+	(_dlfcn_hook): New extern decl.
+	(__dlopen, __dlclose, __dlsym, __dlerror, __dladdr, __dladdr1,
+	__dlinfo, __dlmopen, __libc_dlsym_private,
+	__libc_register_dl_open_hook, __libc_register_dlfcn_hook): New
+	prototypes.
+	(__dlvsym): Use DL_CALLER_DECL.
+	* include/libc-symbols.h: Define libdl_hidden_proto and friends.
+
+	* malloc/arena.c (_dl_open_hook): Extern decl.
+	(ptmalloc_init): Don't call _dl_addr when dlopened from statically
+	linked programs but don't use brk for them either.
+
 2004-10-18  Roland McGrath  <roland@redhat.com>
 
 	* dlfcn/bug-dlsym1.c (main): Remove bogus setenv call.
diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 4330a8a59d..ed20ae5ccd 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -21,9 +21,11 @@ headers		:= bits/dlfcn.h dlfcn.h
 extra-libs	:= libdl
 libdl-routines	:= dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 dlinfo \
 		   dlmopen
+routines	:= $(patsubst %,s%,$(libdl-routines))
+elide-routines.os := $(routines)
 distribute	:= dlopenold.c glreflib1.c glreflib2.c failtestmod.c \
 		   defaultmod1.c defaultmod2.c errmsg1mod.c modatexit.c \
-		   modcxaatexit.c modstatic.c \
+		   modcxaatexit.c modstatic.c modstatic2.c \
 		   bug-dlsym1-lib1.c bug-dlsym1-lib2.c
 
 extra-libs-others := libdl
@@ -51,10 +53,11 @@ glreflib2.so-no-z-defs = yes
 errmsg1mod.so-no-z-defs = yes
 
 ifeq (yesyesyes,$(build-static)$(build-shared)$(elf))
-tests += tststatic
-tests-static += tststatic
-modules-names += modstatic
+tests += tststatic tststatic2
+tests-static += tststatic tststatic2
+modules-names += modstatic modstatic2
 tststatic-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)elf
+tststatic2-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)elf
 endif
 
 extra-objs += $(modules-names:=.os)
@@ -106,7 +109,12 @@ $(objpfx)modatexit.so: $(common-objpfx)libc.so $(common-objpfx)libc_nonshared.a
 $(objpfx)tststatic: $(objpfx)libdl.a
 $(objpfx)tststatic.out: $(objpfx)tststatic $(objpfx)modstatic.so
 
-$(objpfx)modstatic.so: $(common-objpfx)libc.so $(common-objpfx)libc_nonshared.a
+$(objpfx)tststatic2: $(objpfx)libdl.a
+$(objpfx)tststatic2.out: $(objpfx)tststatic2 $(objpfx)modstatic.so \
+			 $(objpfx)modstatic2.so
+
+$(objpfx)modstatic2.so: $(libdl) $(common-objpfx)libc.so \
+			$(common-objpfx)libc_nonshared.a
 
 $(objpfx)bug-dlopen1: $(libdl)
 
diff --git a/dlfcn/Versions b/dlfcn/Versions
index 6a41c238aa..97902f0dfd 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -11,4 +11,7 @@ libdl {
   GLIBC_2.3.4 {
     dlmopen;
   }
+  GLIBC_PRIVATE {
+    _dlfcn_hook;
+  }
 }
diff --git a/dlfcn/dladdr.c b/dlfcn/dladdr.c
index b5490e5a8d..d0462b94ff 100644
--- a/dlfcn/dladdr.c
+++ b/dlfcn/dladdr.c
@@ -1,5 +1,6 @@
 /* Locate the shared object symbol nearest a given address.
-   Copyright (C) 1996, 1997, 1998, 1999, 2003 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998, 1999, 2003, 2004
+   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
@@ -19,8 +20,26 @@
 
 #include <dlfcn.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
 int
 dladdr (const void *address, Dl_info *info)
 {
+  return __dladdr (address, info);
+}
+
+#else
+
+int
+__dladdr (const void *address, Dl_info *info)
+{
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dladdr (address, info);
+# endif
   return _dl_addr (address, info, NULL, NULL);
 }
+# ifdef SHARED
+strong_alias (__dladdr, dladdr)
+# endif
+#endif
diff --git a/dlfcn/dladdr1.c b/dlfcn/dladdr1.c
index 51b53b5861..0f2b603f7c 100644
--- a/dlfcn/dladdr1.c
+++ b/dlfcn/dladdr1.c
@@ -1,5 +1,5 @@
 /* Locate the shared object symbol nearest a given address.
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004 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
@@ -19,9 +19,24 @@
 
 #include <dlfcn.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
 int
 dladdr1 (const void *address, Dl_info *info, void **extra, int flags)
 {
+  return __dladdr1 (address, info, extra, flags);
+}
+
+#else
+
+int
+__dladdr1 (const void *address, Dl_info *info, void **extra, int flags)
+{
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dladdr1 (address, info, extra, flags);
+# endif
+
   switch (flags)
     {
     default:			/* Make this an error?  */
@@ -33,3 +48,7 @@ dladdr1 (const void *address, Dl_info *info, void **extra, int flags)
       return _dl_addr (address, info, (struct link_map **) extra, NULL);
     }
 }
+# ifdef SHARED
+strong_alias (__dladdr1, dladdr1)
+# endif
+#endif
diff --git a/dlfcn/dlclose.c b/dlfcn/dlclose.c
index 046e6d4d98..3ddedcffbe 100644
--- a/dlfcn/dlclose.c
+++ b/dlfcn/dlclose.c
@@ -1,5 +1,6 @@
 /* Close a handle opened by `dlopen'.
-   Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1997, 1998, 1999, 2004
+   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
@@ -19,6 +20,16 @@
 
 #include <dlfcn.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
+int
+dlclose (void *handle)
+{
+  return __dlclose (handle);
+}
+
+#else
+
 static void
 dlclose_doit (void *handle)
 {
@@ -26,7 +37,16 @@ dlclose_doit (void *handle)
 }
 
 int
-dlclose (void *handle)
+__dlclose (void *handle)
 {
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dlclose (handle);
+# endif
+
   return _dlerror_run (dlclose_doit, handle) ? -1 : 0;
 }
+# ifdef SHARED
+strong_alias (__dlclose, dlclose)
+# endif
+#endif
diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c
index 1cde04b080..8789f4f68b 100644
--- a/dlfcn/dlerror.c
+++ b/dlfcn/dlerror.c
@@ -25,6 +25,16 @@
 #include <bits/libc-lock.h>
 #include <ldsodefs.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
+char *
+dlerror (void)
+{
+  return __dlerror ();
+}
+
+#else
+
 /* Type for storing results of dynamic loading actions.  */
 struct dl_action_result
   {
@@ -46,11 +56,16 @@ static void free_key_mem (void *mem);
 
 
 char *
-dlerror (void)
+__dlerror (void)
 {
   char *buf = NULL;
   struct dl_action_result *result;
 
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dlerror ();
+# endif
+
   /* If we have not yet initialized the buffer do it now.  */
   __libc_once (once, init);
 
@@ -99,6 +114,9 @@ dlerror (void)
 
   return buf;
 }
+# ifdef SHARED
+strong_alias (__dlerror, dlerror)
+# endif
 
 int
 internal_function
@@ -185,3 +203,35 @@ free_key_mem (void *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
diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c
index d54a13259a..44af55a303 100644
--- a/dlfcn/dlinfo.c
+++ b/dlfcn/dlinfo.c
@@ -22,6 +22,16 @@
 #include <ldsodefs.h>
 #include <libintl.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
+int
+dlinfo (void *handle, int request, void *arg)
+{
+  return __dlinfo (handle, request, arg, RETURN_ADDRESS (0));
+}
+
+#else
+
 struct dlinfo_args
 {
   ElfW(Addr) caller;
@@ -36,7 +46,7 @@ dlinfo_doit (void *argsblock)
   struct dlinfo_args *const args = argsblock;
   struct link_map *l = args->handle;
 
-#if 0
+# if 0
   if (args->handle == RTLD_SELF)
     {
       Lmid_t nsid;
@@ -53,7 +63,7 @@ dlinfo_doit (void *argsblock)
 	GLRO(dl_signal_error) (0, NULL, NULL, N_("\
 RTLD_SELF used in code not dynamically loaded"));
     }
-#endif
+# endif
 
   switch (args->request)
     {
@@ -84,9 +94,19 @@ RTLD_SELF used in code not dynamically loaded"));
 }
 
 int
-dlinfo (void *handle, int request, void *arg)
+__dlinfo (void *handle, int request, void *arg DL_CALLER_DECL)
 {
-  struct dlinfo_args args = { (ElfW(Addr)) RETURN_ADDRESS (0),
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dlinfo (handle, request, arg,
+				DL_CALLER);
+# endif
+
+  struct dlinfo_args args = { (ElfW(Addr)) DL_CALLER,
 			      handle, request, arg };
   return _dlerror_run (&dlinfo_doit, &args) ? -1 : 0;
 }
+# ifdef SHARED
+strong_alias (__dlinfo, dlinfo)
+# endif
+#endif
diff --git a/dlfcn/dlmopen.c b/dlfcn/dlmopen.c
index fb2a50bb1d..5fd6543655 100644
--- a/dlfcn/dlmopen.c
+++ b/dlfcn/dlmopen.c
@@ -23,6 +23,17 @@
 #include <stddef.h>
 #include <ldsodefs.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
+void *
+dlmopen (Lmid_t nsid, const char *file, int mode)
+{
+  return __dlmopen (nsid, file, mode, RETURN_ADDRESS (0));
+}
+static_link_warning (dlmopen)
+
+#else
+
 struct dlmopen_args
 {
   /* Namespace ID.  */
@@ -43,11 +54,11 @@ dlmopen_doit (void *a)
 
   /* Non-shared code has no support for multiple namespaces.  */
   if (args->nsid != LM_ID_BASE)
-#ifdef SHARED
+# ifdef SHARED
     /* If trying to open the link map for the main executable the namespace
        must be the main one.  */
     if (args->file == NULL)
-#endif
+# endif
       GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("invalid namespace"));
 
   args->new = _dl_open (args->file ?: "", args->mode | __RTLD_DLOPEN,
@@ -56,14 +67,32 @@ dlmopen_doit (void *a)
 
 
 void *
-dlmopen (Lmid_t nsid, const char *file, int mode)
+__dlmopen (Lmid_t nsid, const char *file, int mode DL_CALLER_DECL)
 {
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dlmopen (nsid, file, mode, RETURN_ADDRESS (0));
+# endif
+
   struct dlmopen_args args;
   args.nsid = nsid;
   args.file = file;
   args.mode = mode;
-  args.caller = RETURN_ADDRESS (0);
+  args.caller = DL_CALLER;
 
+# ifdef SHARED
   return _dlerror_run (dlmopen_doit, &args) ? NULL : args.new;
+# else
+  if (_dlerror_run (dlmopen_doit, &args))
+    return NULL;
+
+  __libc_register_dl_open_hook ((struct link_map *) args.new);
+  __libc_register_dlfcn_hook ((struct link_map *) args.new);
+
+  return args.new;
+# endif
 }
-static_link_warning (dlmopen)
+# ifdef SHARED
+strong_alias (__dlmopen, dlmopen)
+# endif
+#endif
diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c
index cfd7963545..6381ffc9b1 100644
--- a/dlfcn/dlopen.c
+++ b/dlfcn/dlopen.c
@@ -20,6 +20,17 @@
 #include <dlfcn.h>
 #include <stddef.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
+void *
+dlopen (const char *file, int mode)
+{
+  return __dlopen (file, mode, RETURN_ADDRESS (0));
+}
+static_link_warning (dlopen)
+
+#else
+
 struct dlopen_args
 {
   /* The arguments for dlopen_doit.  */
@@ -33,11 +44,11 @@ struct dlopen_args
 
 
 /* Non-shared code has no support for multiple namespaces.  */
-#ifdef SHARED
-# define NS __LM_ID_CALLER
-#else
-# define NS LM_ID_BASE
-#endif
+# ifdef SHARED
+#  define NS __LM_ID_CALLER
+# else
+#  define NS LM_ID_BASE
+# endif
 
 
 static void
@@ -50,17 +61,34 @@ dlopen_doit (void *a)
 }
 
 
-extern void *__dlopen_check (const char *file, int mode);
 void *
-__dlopen_check (const char *file, int mode)
+__dlopen (const char *file, int mode DL_CALLER_DECL)
 {
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dlopen (file, mode, DL_CALLER);
+# endif
+
   struct dlopen_args args;
   args.file = file;
   args.mode = mode;
-  args.caller = RETURN_ADDRESS (0);
+  args.caller = DL_CALLER;
 
+# ifdef SHARED
   return _dlerror_run (dlopen_doit, &args) ? NULL : args.new;
+# else
+  if (_dlerror_run (dlopen_doit, &args))
+    return NULL;
+
+  __libc_register_dl_open_hook ((struct link_map *) args.new);
+  __libc_register_dlfcn_hook ((struct link_map *) args.new);
+
+  return args.new;
+# endif
 }
-#include <shlib-compat.h>
+# ifdef SHARED
+#  include <shlib-compat.h>
+strong_alias (__dlopen, __dlopen_check)
 versioned_symbol (libdl, __dlopen_check, dlopen, GLIBC_2_1);
-static_link_warning (dlopen)
+# endif
+#endif
diff --git a/dlfcn/dlopenold.c b/dlfcn/dlopenold.c
index f10674aba3..148716cdb0 100644
--- a/dlfcn/dlopenold.c
+++ b/dlfcn/dlopenold.c
@@ -67,6 +67,9 @@ __dlopen_nocheck (const char *file, int mode)
     mode |= RTLD_LAZY;
   args.mode = mode;
 
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dlopen (file, mode, RETURN_ADDRESS (0));
+
   return _dlerror_run (dlopen_doit, &args) ? NULL : args.new;
 }
 compat_symbol (libdl, __dlopen_nocheck, dlopen, GLIBC_2_0);
diff --git a/dlfcn/dlsym.c b/dlfcn/dlsym.c
index 76dda5973f..a656ca63ce 100644
--- a/dlfcn/dlsym.c
+++ b/dlfcn/dlsym.c
@@ -22,6 +22,16 @@
 
 #include <ldsodefs.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
+void *
+dlsym (void *handle, const char *name)
+{
+  return __dlsym (handle, name, RETURN_ADDRESS (0));
+}
+
+#else
+
 struct dlsym_args
 {
   /* The arguments to dlsym_doit.  */
@@ -43,10 +53,15 @@ dlsym_doit (void *a)
 
 
 void *
-dlsym (void *handle, const char *name)
+__dlsym (void *handle, const char *name DL_CALLER_DECL)
 {
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dlsym (handle, name, DL_CALLER);
+# endif
+
   struct dlsym_args args;
-  args.who = RETURN_ADDRESS (0);
+  args.who = DL_CALLER;
   args.handle = handle;
   args.name = name;
 
@@ -59,3 +74,7 @@ dlsym (void *handle, const char *name)
 
   return result;
 }
+# ifdef SHARED
+strong_alias (__dlsym, dlsym)
+# endif
+#endif
diff --git a/dlfcn/dlvsym.c b/dlfcn/dlvsym.c
index 24868456e9..9f12764e8d 100644
--- a/dlfcn/dlvsym.c
+++ b/dlfcn/dlvsym.c
@@ -22,6 +22,17 @@
 
 #include <ldsodefs.h>
 
+#if !defined SHARED && defined IS_IN_libdl
+
+void *
+weak_function
+dlvsym (void *handle, const char *name, const char *version_str)
+{
+  return __dlvsym (handle, name, version_str, RETURN_ADDRESS (0));
+}
+
+#else
+
 struct dlvsym_args
 {
   /* The arguments to dlvsym_doit.  */
@@ -44,13 +55,18 @@ dlvsym_doit (void *a)
 }
 
 void *
-__dlvsym (void *handle, const char *name, const char *version_str)
+__dlvsym (void *handle, const char *name, const char *version_str
+	  DL_CALLER_DECL)
 {
-  struct dlvsym_args args;
+# ifdef SHARED
+  if (__builtin_expect (_dlfcn_hook != NULL, 0))
+    return _dlfcn_hook->dlvsym (handle, name, version_str, DL_CALLER);
+# endif
 
+  struct dlvsym_args args;
   args.handle = handle;
   args.name = name;
-  args.who = RETURN_ADDRESS (0);
+  args.who = DL_CALLER;
   args.version = version_str;
 
   /* Protect against concurrent loads and unloads.  */
@@ -62,4 +78,7 @@ __dlvsym (void *handle, const char *name, const char *version_str)
 
   return result;
 }
+# ifdef SHARED
 weak_alias (__dlvsym, dlvsym)
+# endif
+#endif
diff --git a/dlfcn/modstatic2.c b/dlfcn/modstatic2.c
new file mode 100644
index 0000000000..0703de851c
--- /dev/null
+++ b/dlfcn/modstatic2.c
@@ -0,0 +1,228 @@
+#include <dlfcn.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnu/lib-names.h>
+
+int test (FILE *out, int a);
+
+int
+test (FILE *out, int a)
+{
+  fputs ("in modstatic2.c (test)\n", out);
+
+  void *handle = dlopen ("modstatic2-nonexistent.so", RTLD_LAZY);
+  if (handle == NULL)
+    fprintf (out, "nonexistent: %s\n", dlerror ());
+  else
+    exit (1);
+
+  handle = dlopen ("modstatic2.so", RTLD_LAZY);
+  if (handle == NULL)
+    {
+      fprintf (out, "%s\n", dlerror ());
+      exit (1);
+    }
+
+  int (*test2) (FILE *, int);
+  test2 = dlsym (handle, "test");
+  if (test2 == NULL)
+    {
+      fprintf (out, "%s\n", dlerror ());
+      exit (1);
+    }
+  if (test2 != test)
+    {
+      fprintf (out, "test %p != test2 %p\n", test, test2);
+      exit (1);
+    }
+
+  Dl_info info;
+  int res = dladdr (test2, &info);
+  if (res == 0)
+    {
+      fputs ("dladdr returned 0\n", out);
+      exit (1);
+    }
+  else
+    {
+      if (strstr (info.dli_fname, "modstatic2.so") == NULL
+	  || strcmp (info.dli_sname, "test") != 0)
+	{
+	  fprintf (out, "fname %s sname %s\n", info.dli_fname, info.dli_sname);
+	  exit (1);
+	}
+      if (info.dli_saddr != (void *) test2)
+	{
+	  fprintf (out, "saddr %p != test %p\n", info.dli_saddr, test2);
+	  exit (1);
+	}
+    }
+
+  ElfW(Sym) *sym;
+  void *symp;
+  res = dladdr1 (test2, &info, &symp, RTLD_DL_SYMENT);
+  if (res == 0)
+    {
+      fputs ("dladdr1 returned 0\n", out);
+      exit (1);
+    }
+  else
+    {
+      if (strstr (info.dli_fname, "modstatic2.so") == NULL
+	  || strcmp (info.dli_sname, "test") != 0)
+	{
+	  fprintf (out, "fname %s sname %s\n", info.dli_fname, info.dli_sname);
+	  exit (1);
+	}
+      if (info.dli_saddr != (void *) test2)
+	{
+	  fprintf (out, "saddr %p != test %p\n", info.dli_saddr, test2);
+	  exit (1);
+	}
+      sym = symp;
+      if (sym == NULL)
+	{
+	  fputs ("sym == NULL\n", out);
+	  exit (1);
+	}
+      if (ELF32_ST_BIND (sym->st_info) != STB_GLOBAL
+	  || ELF32_ST_VISIBILITY (sym->st_other) != STV_DEFAULT)
+	{
+	  fprintf (out, "bind %d visibility %d\n",
+		   (int) ELF32_ST_BIND (sym->st_info),
+		   (int) ELF32_ST_VISIBILITY (sym->st_other));
+	  exit (1);
+	}
+    }
+
+  Lmid_t lmid;
+  res = dlinfo (handle, RTLD_DI_LMID, &lmid);
+  if (res != 0)
+    {
+      fprintf (out, "dlinfo returned %d %s\n", res, dlerror ());
+      exit (1);
+    }
+  else if (lmid != LM_ID_BASE)
+    {
+      fprintf (out, "lmid %d != %d\n", (int) lmid, (int) LM_ID_BASE);
+      exit (1);
+    }
+
+  void *handle2 = dlopen (LIBDL_SO, RTLD_LAZY);
+  if (handle2 == NULL)
+    {
+      fprintf (out, "libdl.so: %s\n", dlerror ());
+      exit (1);
+    }
+
+#ifdef DO_VERSIONING
+  if (dlvsym (handle2, "_dlfcn_hook", "GLIBC_PRIVATE") == NULL)
+    {
+      fprintf (out, "dlvsym: %s\n", dlerror ());
+      exit (1);
+    }
+#endif
+
+  void *(*dlsymfn) (void *, const char *);
+  dlsymfn = dlsym (handle2, "dlsym");
+  if (dlsymfn == NULL)
+    {
+      fprintf (out, "dlsym \"dlsym\": %s\n", dlerror ());
+      exit (1);
+    }
+  void *test3 = dlsymfn (handle, "test");
+  if (test3 == NULL)
+    {
+      fprintf (out, "%s\n", dlerror ());
+      exit (1);
+    }
+  else if (test3 != (void *) test2)
+    {
+      fprintf (out, "test2 %p != test3 %p\n", test2, test3);
+      exit (1);
+    }
+
+  dlclose (handle2);
+  dlclose (handle);
+
+  handle = dlmopen (LM_ID_BASE, "modstatic2.so", RTLD_LAZY);
+  if (handle == NULL)
+    {
+      fprintf (out, "%s\n", dlerror ());
+      exit (1);
+    }
+  dlclose (handle);
+
+  handle = dlmopen (LM_ID_NEWLM, "modstatic2.so", RTLD_LAZY);
+  if (handle == NULL)
+    fprintf (out, "LM_ID_NEWLM: %s\n", dlerror ());
+  else
+    {
+      fputs ("LM_ID_NEWLM unexpectedly succeeded\n", out);
+      exit (1);
+    }
+
+  handle = dlopen ("modstatic.so", RTLD_LAZY);
+  if (handle == NULL)
+    {
+      fprintf (out, "%s\n", dlerror ());
+      exit (1);
+    }
+
+  int (*test4) (int);
+  test4 = dlsym (handle, "test");
+  if (test4 == NULL)
+    {
+      fprintf (out, "%s\n", dlerror ());
+      exit (1);
+    }
+
+  res = test4 (16);
+  if (res != 16 + 16)
+    {
+      fprintf (out, "modstatic.so (test) returned %d\n", res);
+      exit (1);
+    }
+
+  res = dladdr1 (test4, &info, &symp, RTLD_DL_SYMENT);
+  if (res == 0)
+    {
+      fputs ("dladdr1 returned 0\n", out);
+      exit (1);
+    }
+  else
+    {
+      if (strstr (info.dli_fname, "modstatic.so") == NULL
+	  || strcmp (info.dli_sname, "test") != 0)
+	{
+	  fprintf (out, "fname %s sname %s\n", info.dli_fname, info.dli_sname);
+	  exit (1);
+	}
+      if (info.dli_saddr != (void *) test4)
+	{
+	  fprintf (out, "saddr %p != test %p\n", info.dli_saddr, test4);
+	  exit (1);
+	}
+      sym = symp;
+      if (sym == NULL)
+	{
+	  fputs ("sym == NULL\n", out);
+	  exit (1);
+	}
+      if (ELF32_ST_BIND (sym->st_info) != STB_GLOBAL
+	  || ELF32_ST_VISIBILITY (sym->st_other) != STV_DEFAULT)
+	{
+	  fprintf (out, "bind %d visibility %d\n",
+		   (int) ELF32_ST_BIND (sym->st_info),
+		   (int) ELF32_ST_VISIBILITY (sym->st_other));
+	  exit (1);
+	}
+    }
+
+  dlclose (handle);
+
+  fputs ("leaving modstatic2.c (test)\n", out);
+  return a + a;
+}
diff --git a/dlfcn/sdladdr.c b/dlfcn/sdladdr.c
new file mode 100644
index 0000000000..c484d63b29
--- /dev/null
+++ b/dlfcn/sdladdr.c
@@ -0,0 +1 @@
+#include "dladdr.c"
diff --git a/dlfcn/sdladdr1.c b/dlfcn/sdladdr1.c
new file mode 100644
index 0000000000..a655979bcf
--- /dev/null
+++ b/dlfcn/sdladdr1.c
@@ -0,0 +1 @@
+#include "dladdr1.c"
diff --git a/dlfcn/sdlclose.c b/dlfcn/sdlclose.c
new file mode 100644
index 0000000000..dc89b98029
--- /dev/null
+++ b/dlfcn/sdlclose.c
@@ -0,0 +1 @@
+#include "dlclose.c"
diff --git a/dlfcn/sdlerror.c b/dlfcn/sdlerror.c
new file mode 100644
index 0000000000..f1226a48c5
--- /dev/null
+++ b/dlfcn/sdlerror.c
@@ -0,0 +1 @@
+#include "dlerror.c"
diff --git a/dlfcn/sdlinfo.c b/dlfcn/sdlinfo.c
new file mode 100644
index 0000000000..dcc257dd1f
--- /dev/null
+++ b/dlfcn/sdlinfo.c
@@ -0,0 +1 @@
+#include "dlinfo.c"
diff --git a/dlfcn/sdlmopen.c b/dlfcn/sdlmopen.c
new file mode 100644
index 0000000000..9630c89a7f
--- /dev/null
+++ b/dlfcn/sdlmopen.c
@@ -0,0 +1 @@
+#include "dlmopen.c"
diff --git a/dlfcn/sdlopen.c b/dlfcn/sdlopen.c
new file mode 100644
index 0000000000..1ff2eb2854
--- /dev/null
+++ b/dlfcn/sdlopen.c
@@ -0,0 +1 @@
+#include "dlopen.c"
diff --git a/dlfcn/sdlsym.c b/dlfcn/sdlsym.c
new file mode 100644
index 0000000000..0234f23f8f
--- /dev/null
+++ b/dlfcn/sdlsym.c
@@ -0,0 +1 @@
+#include "dlsym.c"
diff --git a/dlfcn/sdlvsym.c b/dlfcn/sdlvsym.c
new file mode 100644
index 0000000000..ec4286058c
--- /dev/null
+++ b/dlfcn/sdlvsym.c
@@ -0,0 +1 @@
+#include "dlvsym.c"
diff --git a/dlfcn/tststatic2.c b/dlfcn/tststatic2.c
new file mode 100644
index 0000000000..85c0fb2ff9
--- /dev/null
+++ b/dlfcn/tststatic2.c
@@ -0,0 +1,166 @@
+#include <dlfcn.h>
+#include <link.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnu/lib-names.h>
+
+int
+main (void)
+{
+  void *handle = dlopen ("modstatic2-nonexistent.so", RTLD_LAZY);
+  if (handle == NULL)
+    printf ("nonexistent: %s\n", dlerror ());
+  else
+    exit (1);
+
+  handle = dlopen ("modstatic2.so", RTLD_LAZY);
+  if (handle == NULL)
+    {
+      printf ("%s\n", dlerror ());
+      exit (1);
+    }
+
+  int (*test) (FILE *, int);
+  test = dlsym (handle, "test");
+  if (test == NULL)
+    {
+      printf ("%s\n", dlerror ());
+      exit (1);
+    }
+
+  Dl_info info;
+  int res = dladdr (test, &info);
+  if (res == 0)
+    {
+      puts ("dladdr returned 0");
+      exit (1);
+    }
+  else
+    {
+      if (strstr (info.dli_fname, "modstatic2.so") == NULL
+	  || strcmp (info.dli_sname, "test") != 0)
+	{
+	  printf ("fname %s sname %s\n", info.dli_fname, info.dli_sname);
+	  exit (1);
+	}
+      if (info.dli_saddr != (void *) test)
+	{
+	  printf ("saddr %p != test %p\n", info.dli_saddr, test);
+	  exit (1);
+	}
+    }
+
+  ElfW(Sym) *sym;
+  void *symp;
+  res = dladdr1 (test, &info, &symp, RTLD_DL_SYMENT);
+  if (res == 0)
+    {
+      puts ("dladdr1 returned 0");
+      exit (1);
+    }
+  else
+    {
+      if (strstr (info.dli_fname, "modstatic2.so") == NULL
+	  || strcmp (info.dli_sname, "test") != 0)
+	{
+	  printf ("fname %s sname %s\n", info.dli_fname, info.dli_sname);
+	  exit (1);
+	}
+      if (info.dli_saddr != (void *) test)
+	{
+	  printf ("saddr %p != test %p\n", info.dli_saddr, test);
+	  exit (1);
+	}
+      sym = symp;
+      if (sym == NULL)
+	{
+	  puts ("sym == NULL\n");
+	  exit (1);
+	}
+      if (ELF32_ST_BIND (sym->st_info) != STB_GLOBAL
+	  || ELF32_ST_VISIBILITY (sym->st_other) != STV_DEFAULT)
+	{
+	  printf ("bind %d visibility %d\n",
+		  (int) ELF32_ST_BIND (sym->st_info),
+		  (int) ELF32_ST_VISIBILITY (sym->st_other));
+	  exit (1);
+	}
+    }
+
+  Lmid_t lmid;
+  res = dlinfo (handle, RTLD_DI_LMID, &lmid);
+  if (res != 0)
+    {
+      printf ("dlinfo returned %d %s\n", res, dlerror ());
+      exit (1);
+    }
+  else if (lmid != LM_ID_BASE)
+    {
+      printf ("lmid %d != %d\n", (int) lmid, (int) LM_ID_BASE);
+      exit (1);
+    }
+
+  res = test (stdout, 2);
+  if (res != 4)
+    {
+      printf ("Got %i, expected 4\n", res);
+      exit (1);
+    }
+
+  void *handle2 = dlopen (LIBDL_SO, RTLD_LAZY);
+  if (handle2 == NULL)
+    {
+      printf ("libdl.so: %s\n", dlerror ());
+      exit (1);
+    }
+
+#ifdef DO_VERSIONING
+  if (dlvsym (handle2, "_dlfcn_hook", "GLIBC_PRIVATE") == NULL)
+    {
+      printf ("dlvsym: %s\n", dlerror ());
+      exit (1);
+    }
+#endif
+
+  void *(*dlsymfn) (void *, const char *);
+  dlsymfn = dlsym (handle2, "dlsym");
+  if (dlsymfn == NULL)
+    {
+      printf ("dlsym \"dlsym\": %s\n", dlerror ());
+      exit (1);
+    }
+  void *test2 = dlsymfn (handle, "test");
+  if (test2 == NULL)
+    {
+      printf ("%s\n", dlerror ());
+      exit (1);
+    }
+  else if (test2 != (void *) test)
+    {
+      printf ("test %p != test2 %p\n", test, test2);
+      exit (1);
+    }
+
+  dlclose (handle2);
+  dlclose (handle);
+
+  handle = dlmopen (LM_ID_BASE, "modstatic2.so", RTLD_LAZY);
+  if (handle == NULL)
+    {
+      printf ("%s\n", dlerror ());
+      exit (1);
+    }
+  dlclose (handle);
+
+  handle = dlmopen (LM_ID_NEWLM, "modstatic2.so", RTLD_LAZY);
+  if (handle == NULL)
+    printf ("LM_ID_NEWLM: %s\n", dlerror ());
+  else
+    {
+      puts ("LM_ID_NEWLM unexpectedly succeeded");
+      exit (1);
+    }
+
+  return 0;
+}
diff --git a/include/dlfcn.h b/include/dlfcn.h
index d6111c0e76..3ff855a367 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -11,8 +11,6 @@
 #define __LM_ID_CALLER	-2
 
 /* Now define the internal interfaces.  */
-extern void *__dlvsym (void *__handle, __const char *__name,
-		       __const char *__version);
 
 #define __libc_dlopen(name) __libc_dlopen_mode (name, RTLD_LAZY)
 extern void *__libc_dlopen_mode  (__const char *__name, int __mode);
@@ -76,4 +74,62 @@ extern int _dl_catch_error (const char **objname, const char **errstring,
 extern int _dlerror_run (void (*operate) (void *), void *args)
      internal_function;
 
+#ifdef SHARED
+# define DL_CALLER_DECL /* Nothing */
+# define DL_CALLER RETURN_ADDRESS (0)
+#else
+# define DL_CALLER_DECL , void *dl_caller
+# define DL_CALLER dl_caller
+#endif
+
+struct dlfcn_hook
+{
+  void *(*dlopen) (const char *file, int mode, void *dl_caller);
+  int (*dlclose) (void *handle);
+  void *(*dlsym) (void *handle, const char *name, void *dl_caller);
+  void *(*dlvsym) (void *handle, const char *name, const char *version,
+		   void *dl_caller);
+  char *(*dlerror) (void);
+  int (*dladdr) (const void *address, Dl_info *info);
+  int (*dladdr1) (const void *address, Dl_info *info,
+		  void **extra_info, int flags);
+  int (*dlinfo) (void *handle, int request, void *arg, void *dl_caller);
+  void *(*dlmopen) (Lmid_t nsid, const char *file, int mode, void *dl_caller);
+  void *pad[4];
+};
+
+extern struct dlfcn_hook *_dlfcn_hook;
+libdl_hidden_proto (_dlfcn_hook)
+
+extern void *__dlopen (const char *file, int mode DL_CALLER_DECL)
+     attribute_hidden;
+extern void *__dlmopen (Lmid_t nsid, const char *file, int mode DL_CALLER_DECL)
+     attribute_hidden;
+extern int __dlclose (void *handle)
+     attribute_hidden;
+extern void *__dlsym (void *handle, const char *name DL_CALLER_DECL)
+     attribute_hidden;
+extern void *__dlvsym (void *handle, const char *name, const char *version
+		       DL_CALLER_DECL)
+     attribute_hidden;
+extern char *__dlerror (void)
+     attribute_hidden;
+extern int __dladdr (const void *address, Dl_info *info)
+     attribute_hidden;
+extern int __dladdr1 (const void *address, Dl_info *info,
+		      void **extra_info, int flags)
+     attribute_hidden;
+extern int __dlinfo (void *handle, int request, void *arg DL_CALLER_DECL)
+     attribute_hidden;
+
+#ifndef SHARED
+struct link_map;
+extern void * __libc_dlsym_private (struct link_map *map, const char *name)
+     attribute_hidden;
+extern void __libc_register_dl_open_hook (struct link_map *map)
+     attribute_hidden;
+extern void __libc_register_dlfcn_hook (struct link_map *map)
+     attribute_hidden;
+#endif
+
 #endif
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index e7880b6c46..7dd0d2901b 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -762,6 +762,24 @@ for linking")
 # define libresolv_hidden_data_ver(local, name)
 #endif
 
+#if defined NOT_IN_libc && defined IS_IN_libdl
+# define libdl_hidden_proto(name, attrs...) hidden_proto (name, ##attrs)
+# define libdl_hidden_def(name) hidden_def (name)
+# define libdl_hidden_weak(name) hidden_weak (name)
+# define libdl_hidden_ver(local, name) hidden_ver (local, name)
+# define libdl_hidden_data_def(name) hidden_data_def (name)
+# define libdl_hidden_data_weak(name) hidden_data_weak (name)
+# define libdl_hidden_data_ver(local, name) hidden_data_ver (local, name)
+#else
+# define libdl_hidden_proto(name, attrs...)
+# define libdl_hidden_def(name)
+# define libdl_hidden_weak(name)
+# define libdl_hidden_ver(local, name)
+# define libdl_hidden_data_def(name)
+# define libdl_hidden_data_weak(name)
+# define libdl_hidden_data_ver(local, name)
+#endif
+
 #ifdef HAVE_BUILTIN_REDIRECTION
 # define libc_hidden_builtin_proto(name, attrs...) libc_hidden_proto (name, ##attrs)
 # define libc_hidden_builtin_def(name) libc_hidden_def (name)
diff --git a/malloc/arena.c b/malloc/arena.c
index 9018a4f0e9..024e191b9e 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -347,6 +347,9 @@ __failing_morecore (ptrdiff_t d)
 {
   return (void *) MORECORE_FAILURE;
 }
+
+extern struct dl_open_hook *_dl_open_hook;
+libc_hidden_proto (_dl_open_hook);
 # endif
 
 # if defined SHARED && defined USE_TLS && !USE___THREAD
@@ -429,10 +432,14 @@ ptmalloc_init (void)
   main_arena.next = &main_arena;
 
 #if defined _LIBC && defined SHARED
-  /* In case this libc copy is in a non-default namespace, never use brk.  */
+  /* In case this libc copy is in a non-default namespace, never use brk.
+     Likewise if dlopened from statically linked program.  */
   Dl_info di;
   struct link_map *l;
-  if (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0 && l->l_ns != LM_ID_BASE)
+
+  if (_dl_open_hook != NULL
+      || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
+	  && l->l_ns != LM_ID_BASE))
     __morecore = __failing_morecore;
 #endif