[14/14] dlfcn: Rework static dlopen hooks

Message ID cea3b14275930d7bf1aaed9dc8cc893424787444.1621953727.git.fweimer@redhat.com
State Superseded
Headers
Series Move libdl into libc |

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Florian Weimer May 25, 2021, 2:46 p.m. UTC
  Consolidate all hooks structures into a single one.  There are
no static dlopen ABI concerns because glibc 2.34 already comes
with substantial ABI-incompatible chagnes in this area.  (Static
dlopen requires the exact same dynamic glibc version that was used
for static linking.)

The new approach uses a pointer to the hooks structure into
_rtld_global_ro and initalizes it in __rtld_static_init.  This avoids
a back-and-forth with various callback functions.
---
 dlfcn/dladdr.c             |  2 +-
 dlfcn/dladdr1.c            |  2 +-
 dlfcn/dlclose.c            |  2 +-
 dlfcn/dlerror.c            | 32 +----------------
 dlfcn/dlinfo.c             |  2 +-
 dlfcn/dlmopen.c            | 10 ++----
 dlfcn/dlopen.c             | 10 ++----
 dlfcn/dlopenold.c          |  2 +-
 dlfcn/dlsym.c              |  2 +-
 dlfcn/dlvsym.c             |  3 +-
 elf/Versions               |  1 -
 elf/dl-libc.c              | 73 ++++----------------------------------
 elf/rtld_static_init.c     | 18 ++++++++++
 include/dlfcn.h            | 31 ++++++++--------
 sysdeps/generic/ldsodefs.h |  3 ++
 15 files changed, 54 insertions(+), 139 deletions(-)
  

Patch

diff --git a/dlfcn/dladdr.c b/dlfcn/dladdr.c
index 3ef1b7f0b6..1cc305f0c4 100644
--- a/dlfcn/dladdr.c
+++ b/dlfcn/dladdr.c
@@ -25,7 +25,7 @@  __dladdr (const void *address, Dl_info *info)
 {
 #ifdef SHARED
   if (!rtld_active ())
-    return _dlfcn_hook->dladdr (address, info);
+    return GLRO (dl_dlfcn_hook)->dladdr (address, info);
 #endif
   return _dl_addr (address, info, NULL, NULL);
 }
diff --git a/dlfcn/dladdr1.c b/dlfcn/dladdr1.c
index 203d6398e4..78560dbac2 100644
--- a/dlfcn/dladdr1.c
+++ b/dlfcn/dladdr1.c
@@ -25,7 +25,7 @@  __dladdr1 (const void *address, Dl_info *info, void **extra, int flags)
 {
 #ifdef SHARED
   if (!rtld_active ())
-    return _dlfcn_hook->dladdr1 (address, info, extra, flags);
+    return GLRO (dl_dlfcn_hook)->dladdr1 (address, info, extra, flags);
 #endif
 
   switch (flags)
diff --git a/dlfcn/dlclose.c b/dlfcn/dlclose.c
index 4d5d307ab1..6a013a81bb 100644
--- a/dlfcn/dlclose.c
+++ b/dlfcn/dlclose.c
@@ -25,7 +25,7 @@  __dlclose (void *handle)
 {
 #ifdef SHARED
   if (!rtld_active ())
-    return _dlfcn_hook->dlclose (handle);
+    return GLRO (dl_dlfcn_hook)->dlclose (handle);
 #endif
 
   return _dlerror_run (GLRO (dl_close), handle) ? -1 : 0;
diff --git a/dlfcn/dlerror.c b/dlfcn/dlerror.c
index 3df8602f4d..d0194a7cef 100644
--- a/dlfcn/dlerror.c
+++ b/dlfcn/dlerror.c
@@ -33,7 +33,7 @@  __dlerror (void)
 {
 # ifdef SHARED
   if (!rtld_active ())
-    return _dlfcn_hook->dlerror ();
+    return GLRO (dl_dlfcn_hook)->dlerror ();
 # endif
 
   struct dl_action_result *result = __libc_dlerror_result;
@@ -197,33 +197,3 @@  _dlerror_run (void (*operate) (void *), void *args)
     }
 }
 libc_hidden_def (_dlerror_run)
-
-#ifdef SHARED
-struct dlfcn_hook *_dlfcn_hook __attribute__((nocommon));
-libc_hidden_data_def (_dlfcn_hook)
-
-#else /* !SHARED */
-
-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 /* !SHARED */
diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c
index 15fcbc5dc1..c6f9a1da09 100644
--- a/dlfcn/dlinfo.c
+++ b/dlfcn/dlinfo.c
@@ -90,7 +90,7 @@  int
 ___dlinfo (void *handle, int request, void *arg)
 {
   if (!rtld_active ())
-    return _dlfcn_hook->dlinfo (handle, request, arg);
+    return GLRO (dl_dlfcn_hook)->dlinfo (handle, request, arg);
   else
     return dlinfo_implementation (handle, request, arg);
 }
diff --git a/dlfcn/dlmopen.c b/dlfcn/dlmopen.c
index ae42814bbf..c171c8953d 100644
--- a/dlfcn/dlmopen.c
+++ b/dlfcn/dlmopen.c
@@ -81,7 +81,7 @@  void *
 ___dlmopen (Lmid_t nsid, const char *file, int mode)
 {
   if (!rtld_active ())
-    return _dlfcn_hook->dlmopen (nsid, file, mode, RETURN_ADDRESS (0));
+    return GLRO (dl_dlfcn_hook)->dlmopen (nsid, file, mode, RETURN_ADDRESS (0));
   else
     return dlmopen_implementation (nsid, file, mode, RETURN_ADDRESS (0));
 }
@@ -101,13 +101,7 @@  __dlmopen (Lmid_t nsid, const char *file, int mode, void *dl_caller)
 void *
 ___dlmopen (Lmid_t nsid, const char *file, int mode)
 {
-  struct link_map *l = __dlmopen (nsid, file, mode, RETURN_ADDRESS (0));
-  if (l != NULL)
-    {
-      __libc_register_dl_open_hook (l);
-      __libc_register_dlfcn_hook (l);
-    }
-  return l;
+  return __dlmopen (nsid, file, mode, RETURN_ADDRESS (0));
 }
 weak_alias (___dlmopen, dlmopen)
 static_link_warning (dlmopen)
diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c
index afdc113efb..e04b374b82 100644
--- a/dlfcn/dlopen.c
+++ b/dlfcn/dlopen.c
@@ -76,7 +76,7 @@  void *
 ___dlopen (const char *file, int mode)
 {
   if (!rtld_active ())
-    return _dlfcn_hook->dlopen (file, mode, RETURN_ADDRESS (0));
+    return GLRO (dl_dlfcn_hook)->dlopen (file, mode, RETURN_ADDRESS (0));
   else
     return dlopen_implementation (file, mode, RETURN_ADDRESS (0));
 }
@@ -96,13 +96,7 @@  __dlopen (const char *file, int mode, void *dl_caller)
 void *
 ___dlopen (const char *file, int mode)
 {
-  struct link_map *l = __dlopen (file, mode, RETURN_ADDRESS (0));
-  if (l != NULL)
-    {
-      __libc_register_dl_open_hook (l);
-      __libc_register_dlfcn_hook (l);
-    }
-  return l;
+  return __dlopen (file, mode, RETURN_ADDRESS (0));
 }
 weak_alias (___dlopen, dlopen)
 static_link_warning (dlopen)
diff --git a/dlfcn/dlopenold.c b/dlfcn/dlopenold.c
index 0fe5f24cc5..9115501ac1 100644
--- a/dlfcn/dlopenold.c
+++ b/dlfcn/dlopenold.c
@@ -71,7 +71,7 @@  __dlopen_nocheck (const char *file, int mode)
   args.mode = mode;
 
   if (!rtld_active ())
-    return _dlfcn_hook->dlopen (file, mode, RETURN_ADDRESS (0));
+    return GLRO (dl_dlfcn_hook)->dlopen (file, mode, RETURN_ADDRESS (0));
 
   return _dlerror_run (dlopen_doit, &args) ? NULL : args.new;
 }
diff --git a/dlfcn/dlsym.c b/dlfcn/dlsym.c
index 6b03b7b7ab..43044cf7bb 100644
--- a/dlfcn/dlsym.c
+++ b/dlfcn/dlsym.c
@@ -63,7 +63,7 @@  void *
 ___dlsym (void *handle, const char *name)
 {
   if (!rtld_active ())
-    return _dlfcn_hook->dlsym (handle, name, RETURN_ADDRESS (0));
+    return GLRO (dl_dlfcn_hook)->dlsym (handle, name, RETURN_ADDRESS (0));
   else
     return dlsym_implementation (handle, name, RETURN_ADDRESS (0));
 }
diff --git a/dlfcn/dlvsym.c b/dlfcn/dlvsym.c
index de6b340647..9b76f9afa5 100644
--- a/dlfcn/dlvsym.c
+++ b/dlfcn/dlvsym.c
@@ -66,7 +66,8 @@  void *
 ___dlvsym (void *handle, const char *name, const char *version)
 {
   if (!rtld_active ())
-    return _dlfcn_hook->dlvsym (handle, name, version, RETURN_ADDRESS (0));
+    return GLRO (dl_dlfcn_hook)->dlvsym (handle, name, version,
+					 RETURN_ADDRESS (0));
   else
     return dlvsym_implementation (handle, name, version, RETURN_ADDRESS (0));
 }
diff --git a/elf/Versions b/elf/Versions
index be88c48e6d..a12d64e8db 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -23,7 +23,6 @@  libc {
   GLIBC_PRIVATE {
     # functions used in other libraries
     _dl_addr;
-    _dl_open_hook; _dl_open_hook2;
     _dl_sym; _dl_vsym;
     __libc_dlclose; __libc_dlopen_mode; __libc_dlsym; __libc_dlvsym;
     __libc_early_init;
diff --git a/elf/dl-libc.c b/elf/dl-libc.c
index ed551f6e56..3ac2a0645f 100644
--- a/elf/dl-libc.c
+++ b/elf/dl-libc.c
@@ -126,32 +126,7 @@  do_dlclose (void *ptr)
   GLRO(dl_close) ((struct link_map *) ptr);
 }
 
-/* This code is to support __libc_dlopen from __libc_dlopen'ed shared
-   libraries.  We need to ensure the statically linked __libc_dlopen
-   etc. functions are used instead of the dynamically loaded.  */
-struct dl_open_hook
-{
-  void *(*dlopen_mode) (const char *name, int mode);
-  void *(*dlsym) (void *map, const char *name);
-  int (*dlclose) (void *map);
-  void *(*dlvsym) (void *map, const char *name, const char *version);
-};
-
-#ifdef SHARED
-extern struct dl_open_hook *_dl_open_hook;
-libc_hidden_proto (_dl_open_hook);
-struct dl_open_hook *_dl_open_hook __attribute__ ((nocommon));
-libc_hidden_data_def (_dl_open_hook);
-
-/* The dlvsym member was added retroactively to struct dl_open_hook.
-   Static applications which have it will set _dl_open_hook2 in
-   addition to _dl_open_hook.  */
-extern struct dl_open_hook *_dl_open_hook2;
-libc_hidden_proto (_dl_open_hook2);
-struct dl_open_hook *_dl_open_hook2 __attribute__ ((nocommon));
-libc_hidden_data_def (_dl_open_hook2);
-
-#else
+#ifndef SHARED
 static void
 do_dlsym_private (void *ptr)
 {
@@ -169,14 +144,6 @@  do_dlsym_private (void *ptr)
 				args->map->l_scope, &vers, 0, 0, NULL);
   args->loadbase = l;
 }
-
-static struct dl_open_hook _dl_open_hook =
-  {
-    .dlopen_mode = __libc_dlopen_mode,
-    .dlsym = __libc_dlsym,
-    .dlclose = __libc_dlclose,
-    .dlvsym = __libc_dlvsym,
-  };
 #endif
 
 /* ... and these functions call dlerror_run. */
@@ -191,16 +158,9 @@  __libc_dlopen_mode (const char *name, int mode)
 
 #ifdef SHARED
   if (!rtld_active ())
-    return _dl_open_hook->dlopen_mode (name, mode);
-  return (dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map);
-#else
-  if (dlerror_run (do_dlopen, &args))
-    return NULL;
-
-  __libc_register_dl_open_hook (args.map);
-  __libc_register_dlfcn_hook (args.map);
-  return (void *) args.map;
+    return GLRO (dl_dlfcn_hook)->libc_dlopen_mode (name, mode);
 #endif
+  return (dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map);
 }
 libc_hidden_def (__libc_dlopen_mode)
 
@@ -216,21 +176,6 @@  __libc_dlsym_private (struct link_map *map, const char *name)
     return DL_SYMBOL_ADDRESS (sargs.loadbase, sargs.ref);
   return NULL;
 }
-
-void
-__libc_register_dl_open_hook (struct link_map *map)
-{
-  struct dl_open_hook **hook;
-
-  hook = (struct dl_open_hook **) __libc_dlsym_private (map, "_dl_open_hook");
-  if (hook != NULL)
-    *hook = &_dl_open_hook;
-
-  /* For dlvsym support.  */
-  hook = (struct dl_open_hook **) __libc_dlsym_private (map, "_dl_open_hook2");
-  if (hook != NULL)
-    *hook = &_dl_open_hook;
-}
 #endif
 
 void *
@@ -242,7 +187,7 @@  __libc_dlsym (void *map, const char *name)
 
 #ifdef SHARED
   if (!rtld_active ())
-    return _dl_open_hook->dlsym (map, name);
+    return GLRO (dl_dlfcn_hook)->libc_dlsym (map, name);
 #endif
   return (dlerror_run (do_dlsym, &args) ? NULL
 	  : (void *) (DL_SYMBOL_ADDRESS (args.loadbase, args.ref)));
@@ -257,13 +202,7 @@  __libc_dlvsym (void *map, const char *name, const char *version)
 {
 #ifdef SHARED
   if (!rtld_active ())
-    {
-      /* The static application is too old and does not provide the
-	 dlvsym hook.  */
-      if (_dl_open_hook2 == NULL)
-	return NULL;
-      return _dl_open_hook2->dlvsym (map, name, version);
-    }
+    return GLRO (dl_dlfcn_hook)->libc_dlvsym (map, name, version);
 #endif
 
   struct do_dlvsym_args args;
@@ -287,7 +226,7 @@  __libc_dlclose (void *map)
 {
 #ifdef SHARED
   if (!rtld_active ())
-    return _dl_open_hook->dlclose (map);
+    return GLRO (dl_dlfcn_hook)->libc_dlclose (map);
 #endif
   return dlerror_run (do_dlclose, map);
 }
diff --git a/elf/rtld_static_init.c b/elf/rtld_static_init.c
index 42efecfbff..3f8abb6800 100644
--- a/elf/rtld_static_init.c
+++ b/elf/rtld_static_init.c
@@ -25,6 +25,23 @@ 
 
 #include <rtld_static_init.h>
 
+static const struct dlfcn_hook _dlfcn_hook =
+  {
+    .dlopen = __dlopen,
+    .dlclose = __dlclose,
+    .dlsym = __dlsym,
+    .dlvsym = __dlvsym,
+    .dlerror = __dlerror,
+    .dladdr = __dladdr,
+    .dladdr1 = __dladdr1,
+    .dlinfo = __dlinfo,
+    .dlmopen = __dlmopen,
+    .libc_dlopen_mode = __libc_dlopen_mode,
+    .libc_dlsym = __libc_dlsym,
+    .libc_dlvsym = __libc_dlvsym,
+    .libc_dlclose = __libc_dlclose,
+  };
+
 void
 __rtld_static_init (struct link_map *map)
 {
@@ -45,6 +62,7 @@  __rtld_static_init (struct link_map *map)
   extern __typeof (dl->_dl_clktck) _dl_clktck attribute_hidden;
   dl->_dl_clktck = _dl_clktck;
 #endif
+  dl->_dl_dlfcn_hook = &_dlfcn_hook;
   extern __typeof (dl->_dl_hwcap) _dl_hwcap attribute_hidden;
   dl->_dl_hwcap = _dl_hwcap;
   extern __typeof (dl->_dl_hwcap2) _dl_hwcap2 attribute_hidden;
diff --git a/include/dlfcn.h b/include/dlfcn.h
index 711bbb0f12..d4440c567e 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -91,8 +91,12 @@  libc_hidden_proto (_dl_vsym)
 extern int _dlerror_run (void (*operate) (void *), void *args);
 libc_hidden_proto (_dlerror_run)
 
+/* This structure is used to make the outer (statically linked)
+   implementation of dlopen and related functions to the inner libc
+   after static dlopen, via the GLRO (dl_dlfcn_hook) pointer.  */
 struct dlfcn_hook
 {
+  /* Public interfaces.  */
   void *(*dlopen) (const char *file, int mode, void *dl_caller);
   int (*dlclose) (void *handle);
   void *(*dlsym) (void *handle, const char *name, void *dl_caller);
@@ -104,15 +108,17 @@  struct dlfcn_hook
 		  void **extra_info, int flags);
   int (*dlinfo) (void *handle, int request, void *arg);
   void *(*dlmopen) (Lmid_t nsid, const char *file, int mode, void *dl_caller);
-  void *pad[4];
-};
 
-extern struct dlfcn_hook *_dlfcn_hook;
-libc_hidden_proto (_dlfcn_hook)
+  /* Internal interfaces.  */
+  void* (*libc_dlopen_mode)  (const char *__name, int __mode);
+  void* (*libc_dlsym)  (void *map, const char *name);
+  void* (*libc_dlvsym)  (void *map, const char *name, const char *version);
+  int   (*libc_dlclose) (void *map);
+};
 
-/* Note: These prototypes are for initializing _dflcn_hook in static
-   libraries.  Internal calls in glibc should use the __libc_dl*
-   functions defined in elf/dl-libc.c instead.  */
+/* Note: These prototypes are for initializing _dlfcn_hook in static
+   builds; see __rtld_static_init.  Internal calls in glibc should use
+   the __libc_dl* functions defined in elf/dl-libc.c instead.  */
 
 extern void *__dlopen (const char *file, int mode, void *caller);
 extern void *__dlmopen (Lmid_t nsid, const char *file, int mode,
@@ -125,16 +131,7 @@  extern int __dladdr (const void *address, Dl_info *info);
 extern int __dladdr1 (const void *address, Dl_info *info,
 		      void **extra_info, int flags);
 extern int __dlinfo (void *handle, int request, void *arg);
-
-#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
+extern char *__dlerror (void);
 
 #endif
 #endif
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index e383aa1dc3..176394de4d 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -687,6 +687,9 @@  struct rtld_global_ro
   int (*_dl_discover_osversion) (void);
 #endif
 
+  /* Dynamic linker operations used after static dlopen.  */
+  const struct dlfcn_hook *_dl_dlfcn_hook;
+
   /* List of auditing interfaces.  */
   struct audit_ifaces *_dl_audit;
   unsigned int _dl_naudit;