summary refs log tree commit diff
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>2003-03-15 23:16:22 +0000
committerRoland McGrath <roland@gnu.org>2003-03-15 23:16:22 +0000
commit45e4762c2c7d04cd51213735c2b440c94cdcf28a (patch)
tree5b2c7f008ae7f0a787bf6798bbb7a8663bc7b0ed
parent124dcac84b992d26cfe992f9017f49e92c37add2 (diff)
downloadglibc-45e4762c2c7d04cd51213735c2b440c94cdcf28a.tar.gz
glibc-45e4762c2c7d04cd51213735c2b440c94cdcf28a.tar.xz
glibc-45e4762c2c7d04cd51213735c2b440c94cdcf28a.zip
* dlfcn/tst-dlinfo.c: New file.
	* dlfcn/Makefile (tests): Add tst-dlinfo.
	($(objpfx)tst-dlinfo): New target.

	* dlfcn/dlinfo.c: New file.
	* dlfcn/Makefile (libdl-routines): Add it.
	* dlfcn/Versions (libdl: GLIBC_2.3.3): Add dlinfo.
	* dlfcn/dlfcn.h [__USE_GNU]: Declare dlinfo.
	[__USE_GNU] (RTLD_DI_*): New enum constants.
	[__USE_GNU] (Dl_serpath, Dl_serinfo): New types.
	* elf/dl-load.c (cache_rpath): New inline function.
	(_dl_map_object): Use it.
	(_dl_rtld_di_serinfo): New function.
	* sysdeps/generic/ldsodefs.h: Declare it.
	* elf/Versions (ld: GLIBC_PRIVATE): Add it.
-rw-r--r--ChangeLog16
-rw-r--r--NEWS7
-rw-r--r--dlfcn/Makefile8
-rw-r--r--dlfcn/Versions2
-rw-r--r--dlfcn/dlfcn.h54
-rw-r--r--dlfcn/dlinfo.c87
-rw-r--r--dlfcn/tst-dlinfo.c96
-rw-r--r--elf/Versions2
-rw-r--r--elf/dl-load.c175
-rw-r--r--sysdeps/generic/ldsodefs.h10
10 files changed, 396 insertions, 61 deletions
diff --git a/ChangeLog b/ChangeLog
index 9a793132ee..a387e8f8eb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
 2003-03-15  Roland McGrath  <roland@redhat.com>
 
+	* dlfcn/tst-dlinfo.c: New file.
+	* dlfcn/Makefile (tests): Add tst-dlinfo.
+	($(objpfx)tst-dlinfo): New target.
+
+	* dlfcn/dlinfo.c: New file.
+	* dlfcn/Makefile (libdl-routines): Add it.
+	* dlfcn/Versions (libdl: GLIBC_2.3.3): Add dlinfo.
+	* dlfcn/dlfcn.h [__USE_GNU]: Declare dlinfo.
+	[__USE_GNU] (RTLD_DI_*): New enum constants.
+	[__USE_GNU] (Dl_serpath, Dl_serinfo): New types.
+	* elf/dl-load.c (cache_rpath): New inline function.
+	(_dl_map_object): Use it.
+	(_dl_rtld_di_serinfo): New function.
+	* sysdeps/generic/ldsodefs.h: Declare it.
+	* elf/Versions (ld: GLIBC_PRIVATE): Add it.
+
 	* sysdeps/powerpc/elf/libc-start.c (AUX_VECTOR_INIT): Define it.
 	(LIBC_START_MAIN, LIBC_START_MAIN_AUXVEC_ARG, MAIN_AUXVEC_ARG)
 	(INIT_MAIN_ARGS): Define, and #include <sysdeps/generic/libc-start.c>.
diff --git a/NEWS b/NEWS
index 418949200c..9ac019c660 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,5 @@
-GNU C Library NEWS -- history of user-visible changes.  2003-3-2
-Copyright (C) 1992-2002, 2003 Free Software Foundation, Inc.
+GNU C Library NEWS -- history of user-visible changes.  2003-3-15
+Copyright (C) 1992-2002,2003 Free Software Foundation, Inc.
 See the end for copying conditions.
 
 Please send GNU C library bug reports using the `glibcbug' script to
@@ -7,6 +7,9 @@ Please send GNU C library bug reports using the `glibcbug' script to
 
 Version 2.3.3
 
+* New functions `dladdr1' and `dlinfo' in <dlfcn.h> provide more ways to
+  interrogate the dynamic linker, compatible with the Solaris interface.
+
 * ELF thread-local storage support (TLS) now works on PowerPC and PowerPC64;
   implemented by Paul Mackerras, Steven Munroe, and Roland McGrath.
 
diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index d11336140e..9115d30c2a 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -19,7 +19,8 @@
 subdir		:= dlfcn
 headers		:= bits/dlfcn.h dlfcn.h
 extra-libs	:= libdl
-libdl-routines	:= dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 eval
+libdl-routines	:= dlopen dlclose dlsym dlvsym dlerror dladdr dladdr1 dlinfo \
+		   eval
 distribute	:= dlopenold.c glreflib1.c glreflib2.c failtestmod.c eval.c \
 		   defaultmod1.c defaultmod2.c errmsg1mod.c modatexit.c \
 		   modcxaatexit.c modstatic.c \
@@ -37,7 +38,7 @@ libdl-shared-only-routines += eval
 
 ifeq (yes,$(build-shared))
 tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
-	bug-dlopen1 bug-dlsym1
+	bug-dlopen1 bug-dlsym1 tst-dlinfo
 ifeq (yes,$(have-protected))
 tests += tstatexit
 endif
@@ -74,6 +75,9 @@ $(objpfx)failtest.out: $(objpfx)failtestmod.so
 $(objpfx)tst-dladdr: $(libdl)
 $(objpfx)tst-dladdr.out: $(objpfx)glreflib1.so
 
+$(objpfx)tst-dlinfo: $(libdl)
+$(objpfx)tst-dlinfo.out: $(objpfx)glreflib1.so
+
 LDFLAGS-default = $(LDFLAGS-rdynamic)
 $(objpfx)default: $(libdl) $(objpfx)defaultmod1.so $(objpfx)defaultmod2.so
 $(objpfx)defaultmod1.so: $(libdl) $(common-objpfx)libc_nonshared.a
diff --git a/dlfcn/Versions b/dlfcn/Versions
index ca44a4678f..95ede25e47 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -6,6 +6,6 @@ libdl {
     dlopen; dlvsym;
   }
   GLIBC_2.3.3 {
-    dladdr1;
+    dladdr1; dlinfo;
   }
 }
diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h
index 6c8e4abcc4..9d8ee0d6d1 100644
--- a/dlfcn/dlfcn.h
+++ b/dlfcn/dlfcn.h
@@ -21,6 +21,8 @@
 #define	_DLFCN_H 1
 
 #include <features.h>
+#define __need_size_t
+#include <stddef.h>
 
 /* Collect various system dependent definitions and declarations.  */
 #include <bits/dlfcn.h>
@@ -100,7 +102,57 @@ enum
     RTLD_DL_LINKMAP = 2
   };
 
-#endif
+
+/* Get information about the shared object HANDLE refers to.
+   REQUEST is from among the values below, and determines the use of ARG.
+
+   On success, returns zero.  On failure, returns -1 and records an error
+   message to be fetched with `dlerror'.  */
+extern int dlinfo (void *__restrict __handle,
+		   int __request, void *__restrict __arg);
+
+/* These are the possible values for the REQUEST argument to `dlinfo'.  */
+enum
+  {
+    /* Treat ARG as `struct link_map **';
+       store the `struct link_map *' for HANDLE there.  */
+    RTLD_DI_LINKMAP = 2,
+
+    /* Treat ARG as `Dl_serinfo *' (see below), and fill in to describe the
+       directories that will be searched for dependencies of this object.
+       RTLD_DI_SERINFOSIZE fills in just the `dls_cnt' and `dls_size'
+       entries to indicate the size of the buffer that must be passed to
+       RTLD_DI_SERINFO to fill in the full information.  */
+    RTLD_DI_SERINFO = 4,
+    RTLD_DI_SERINFOSIZE = 5,
+
+    /* Treat ARG as `char *', and store there the directory name used to
+       expand $ORIGIN in this shared object's dependency file names.  */
+    RTLD_DI_ORIGIN = 6,
+
+    RTLD_DI_LMID = 1,		/* Unsupported, defined by Solaris.  */
+    RTLD_DI_CONFIGADDR = 3	/* Unsupported, defined by Solaris.  */
+  };
+
+
+/* This is the type of elements in `Dl_serinfo', below.
+   The `dls_name' member points to space in the buffer passed to `dlinfo'.  */
+typedef struct
+{
+  char *dls_name;		/* Name of library search path directory.  */
+  unsigned int dls_flags;	/* Indicates where this directory came from. */
+} Dl_serpath;
+
+/* This is the structure that must be passed (by reference) to `dlinfo' for
+   the RTLD_DI_SERINFO and RTLD_DI_SERINFOSIZE requests.  */
+typedef struct
+{
+  size_t dls_size;		/* Size in bytes of the whole buffer.  */
+  unsigned int dls_cnt;		/* Number of elements in `dls_serpath'.  */
+  Dl_serpath dls_serpath[1];	/* Actually longer, dls_cnt elements.  */
+} Dl_serinfo;
+#endif /* __USE_GNU */
+
 
 __END_DECLS
 
diff --git a/dlfcn/dlinfo.c b/dlfcn/dlinfo.c
new file mode 100644
index 0000000000..37177f1f1b
--- /dev/null
+++ b/dlfcn/dlinfo.c
@@ -0,0 +1,87 @@
+/* dlinfo -- Get information from the dynamic linker.
+   Copyright (C) 2003 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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <dlfcn.h>
+#include <link.h>
+#include <ldsodefs.h>
+#include <libintl.h>
+
+struct dlinfo_args
+{
+  ElfW(Addr) caller;
+  void *handle;
+  int request;
+  void *arg;
+};
+
+static void
+dlinfo_doit (void *argsblock)
+{
+  struct dlinfo_args *const args = argsblock;
+  struct link_map *l = args->handle;
+
+#if 0
+  if (args->handle == RTLD_SELF)
+    {
+
+      /* Find the highest-addressed object that CALLER is not below.  */
+      for (l = GL(dl_loaded); l != NULL; l = l->l_next)
+	if (caller >= l->l_map_start && caller < l->l_map_end)
+	  /* There must be exactly one DSO for the range of the virtual
+	     memory.  Otherwise something is really broken.  */
+	  break;
+
+      if (l == NULL)
+	_dl_signal_error (0, NULL, NULL, N_("\
+RTLD_SELF used in code not dynamically loaded"));
+    }
+#endif
+
+  switch (args->request)
+    {
+    case RTLD_DI_LMID:
+    case RTLD_DI_CONFIGADDR:
+    default:
+      _dl_signal_error (0, NULL, NULL, N_("unsupported dlinfo request"));
+      break;
+
+    case RTLD_DI_LINKMAP:
+      *(struct link_map **) args->arg = l;
+      break;
+
+    case RTLD_DI_SERINFO:
+      _dl_rtld_di_serinfo (l, args->arg, false);
+      break;
+    case RTLD_DI_SERINFOSIZE:
+      _dl_rtld_di_serinfo (l, args->arg, true);
+      break;
+
+    case RTLD_DI_ORIGIN:
+      strcpy (args->arg, l->l_origin);
+      break;
+    }
+}
+
+int
+dlinfo (void *handle, int request, void *arg)
+{
+  struct dlinfo_args args = { (ElfW(Addr)) RETURN_ADDRESS (0),
+			      handle, request, arg };
+  return _dlerror_run (&dlinfo_doit, &args) ? -1 : 0;
+}
diff --git a/dlfcn/tst-dlinfo.c b/dlfcn/tst-dlinfo.c
new file mode 100644
index 0000000000..70906ebdf1
--- /dev/null
+++ b/dlfcn/tst-dlinfo.c
@@ -0,0 +1,96 @@
+/* Test for dlinfo.
+   Copyright (C) 2003 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, 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 <error.h>
+
+#define TEST_FUNCTION do_test ()
+
+static int
+do_test (void)
+{
+  int status = 0;
+
+  void *handle = dlopen ("glreflib1.so", RTLD_NOW);
+  if (handle == NULL)
+    error (EXIT_FAILURE, 0, "cannot load: glreflib1.so: %s", dlerror ());
+
+#define TRY(req, arg)							      \
+  if (dlinfo (handle, req, arg) != 0)					      \
+    {									      \
+      printf ("dlinfo failed for %s: %s\n", #req, dlerror ());		      \
+      status = 1;							      \
+    }									      \
+  else
+
+  struct link_map *l;
+  TRY (RTLD_DI_LINKMAP, &l)
+    {
+      if (l != handle)
+	{
+	  printf ("bogus link_map? %p != %p\n", l, handle);
+	  status = 1;
+	}
+    }
+
+  char origin[8192];		/* >= PATH_MAX, in theory */
+  TRY (RTLD_DI_ORIGIN, origin)
+    {
+      printf ("origin: %s\n", origin);
+    }
+
+  Dl_serinfo counts;
+  TRY (RTLD_DI_SERINFOSIZE, &counts)
+    {
+      Dl_serinfo *buf = alloca (counts.dls_size);
+      buf->dls_cnt = counts.dls_cnt;
+      buf->dls_size = counts.dls_size;
+      printf ("%u library directories\n", buf->dls_cnt);
+      TRY (RTLD_DI_SERINFO, buf)
+	{
+	  if (counts.dls_cnt != buf->dls_cnt)
+	    {
+	      printf ("??? became %u library directories\n", buf->dls_cnt);
+	      status = 1;
+	    }
+	  for (unsigned int i = 0; i < buf->dls_cnt; ++i)
+	    printf ("\t%#02x\t%s\n",
+		    buf->dls_serpath[i].dls_flags,
+		    buf->dls_serpath[i].dls_name);
+	}
+    }
+
+  unsigned long int lmid = 0xdeadbeefUL;
+  if (dlinfo (handle, RTLD_DI_LMID, &lmid) != 0)
+    printf ("dlinfo refuses RTLD_DI_LMID: %s\n", dlerror ());
+  else
+    {
+      printf ("dlinfo RTLD_DI_LMID worked? %#lx\n", lmid);
+      status = lmid == 0xdeadbeefUL;
+    }
+
+#undef TRY
+  dlclose (handle);
+
+  return status;
+}
+
+#include "../test-skeleton.c"
diff --git a/elf/Versions b/elf/Versions
index b9e99f27fd..e6709bb71b 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -51,6 +51,6 @@ ld {
     _dl_unload_cache;
     _rtld_global; _dl_tls_symaddr; _dl_allocate_tls; _dl_deallocate_tls;
     _dl_get_tls_static_info; _dl_allocate_tls_init;
-    _dl_get_origin; _dl_tls_setup;
+    _dl_get_origin; _dl_tls_setup; _dl_rtld_di_serinfo;
   }
 }
diff --git a/elf/dl-load.c b/elf/dl-load.c
index bc0f942c37..290236b5e1 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -576,6 +576,34 @@ decompose_rpath (struct r_search_path_struct *sps,
   sps->malloced = 1;
 }
 
+/* Make sure cached path information is stored in *SP
+   and return true if there are any paths to search there.  */
+static inline bool
+cache_rpath (struct link_map *l,
+	     struct r_search_path_struct *sp,
+	     int tag,
+	     const char *what)
+{
+  if (sp->dirs == (void *) -1)
+    return false;
+
+  if (sp->dirs != NULL)
+    return true;
+
+  if (l->l_info[tag] == NULL)
+    {
+      /* There is no path.  */
+      sp->dirs = (void *) -1;
+      return false;
+    }
+
+  /* Make sure the cache information is available.  */
+  decompose_rpath (sp, (const char *) (D_PTR (l, l_info[DT_STRTAB])
+				       + l->l_info[tag]->d_un.d_val),
+		   l, what);
+  return true;
+}
+
 
 void
 internal_function
@@ -1747,29 +1775,9 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 	  /* First try the DT_RPATH of the dependent object that caused NAME
 	     to be loaded.  Then that object's dependent, and on up.  */
 	  for (l = loader; fd == -1 && l; l = l->l_loader)
-	    {
-	      if (l->l_rpath_dirs.dirs == NULL)
-		{
-		  if (l->l_info[DT_RPATH] == NULL)
-		    {
-		      /* There is no path.  */
-		      l->l_rpath_dirs.dirs = (void *) -1;
-		      continue;
-		    }
-		  else
-		    {
-		      /* Make sure the cache information is available.  */
-		      size_t ptrval = (D_PTR (l, l_info[DT_STRTAB])
-				       + l->l_info[DT_RPATH]->d_un.d_val);
-		      decompose_rpath (&l->l_rpath_dirs,
-				       (const char *) ptrval, l, "RPATH");
-		    }
-		}
-
-	      if (l->l_rpath_dirs.dirs != (void *) -1)
-		fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs,
-				&realname, &fb);
-	    }
+	    if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH"))
+	      fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs,
+			      &realname, &fb);
 
 	  /* If dynamically linked, try the DT_RPATH of the executable
              itself.  */
@@ -1785,37 +1793,12 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded,
 	fd = open_path (name, namelen, preloaded, &env_path_list,
 			&realname, &fb);
 
-      /* Look at the RUNPATH information for this binary.
-
-	 Note that this is no real loop.  'while' is used only to enable
-	 us to use 'break' instead of a 'goto' to jump to the end.  The
-	 loop is always left after the first round.  */
-      while (fd == -1 && loader != NULL
-	     && loader->l_runpath_dirs.dirs != (void *) -1)
-	{
-	  if (loader->l_runpath_dirs.dirs == NULL)
-	    {
-	      if (loader->l_info[DT_RUNPATH] == NULL)
-		{
-		  /* No RUNPATH.  */
-		  loader->l_runpath_dirs.dirs = (void *) -1;
-		  break;
-		}
-	      else
-		{
-		  /* Make sure the cache information is available.  */
-		  size_t ptrval = (D_PTR (loader, l_info[DT_STRTAB])
-				   + loader->l_info[DT_RUNPATH]->d_un.d_val);
-		  decompose_rpath (&loader->l_runpath_dirs,
-				   (const char *) ptrval, loader, "RUNPATH");
-		}
-	    }
-
-	  if (loader->l_runpath_dirs.dirs != (void *) -1)
-	    fd = open_path (name, namelen, preloaded,
-			    &loader->l_runpath_dirs, &realname, &fb);
-	  break;
-	}
+      /* Look at the RUNPATH information for this binary.  */
+      if (fd == -1 && loader != NULL
+	  && cache_rpath (loader, &loader->l_runpath_dirs,
+			  DT_RUNPATH, "RUNPATH"))
+	fd = open_path (name, namelen, preloaded,
+			&loader->l_runpath_dirs, &realname, &fb);
 
       if (fd == -1
 	  && (__builtin_expect (! preloaded, 1)
@@ -1939,3 +1922,87 @@ cannot create shared object descriptor"));
   return _dl_map_object_from_fd (name, fd, &fb, realname, loader, type, mode);
 }
 INTDEF (_dl_map_object)
+
+void
+internal_function
+_dl_rtld_di_serinfo (struct link_map *loader, Dl_serinfo *si, bool counting)
+{
+  if (counting)
+    {
+      si->dls_cnt = 0;
+      si->dls_size = 0;
+    }
+
+  unsigned int idx = 0;
+  char *allocptr = (char *) &si->dls_serpath[si->dls_cnt];
+  inline void add_path (const struct r_search_path_struct *sps,
+			unsigned int flags)
+# define add_path(sps, flags) add_path(sps, 0) /* XXX */
+    {
+      if (sps->dirs != (void *) -1)
+	{
+	  struct r_search_path_elem **dirs = sps->dirs;
+	  do
+	    {
+	      const struct r_search_path_elem *const r = *dirs++;
+	      if (counting)
+		{
+		  si->dls_cnt++;
+		  si->dls_size += r->dirnamelen;
+		}
+	      else
+		{
+		  Dl_serpath *const sp = &si->dls_serpath[idx++];
+		  sp->dls_name = allocptr;
+		  allocptr = __mempcpy (allocptr,
+					r->dirname, r->dirnamelen - 1);
+		  *allocptr++ = '\0';
+		  sp->dls_flags = flags;
+		}
+	    }
+	  while (*dirs != NULL);
+	}
+    }
+
+  /* When the object has the RUNPATH information we don't use any RPATHs.  */
+  if (loader->l_info[DT_RUNPATH] == NULL)
+    {
+      /* First try the DT_RPATH of the dependent object that caused NAME
+	 to be loaded.  Then that object's dependent, and on up.  */
+
+      struct link_map *l = loader;
+      do
+	{
+	  if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH"))
+	    add_path (&l->l_rpath_dirs, XXX_RPATH);
+	  l = l->l_loader;
+	}
+      while (l != NULL);
+
+      /* If dynamically linked, try the DT_RPATH of the executable itself.  */
+      l = GL(dl_loaded);
+      if (l != NULL && l->l_type != lt_loaded && l != loader)
+	if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH"))
+	  add_path (&l->l_rpath_dirs, XXX_RPATH);
+    }
+
+  /* Try the LD_LIBRARY_PATH environment variable.  */
+  add_path (&env_path_list, XXX_ENV);
+
+  /* Look at the RUNPATH information for this binary.  */
+  if (cache_rpath (loader, &loader->l_runpath_dirs, DT_RUNPATH, "RUNPATH"))
+    add_path (&loader->l_runpath_dirs, XXX_RUNPATH);
+
+  /* XXX
+     Here is where ld.so.cache gets checked, but we don't have
+     a way to indicate that in the results for Dl_serinfo.  */
+
+  /* Finally, try the default path.  */
+  if (!(loader->l_flags_1 & DF_1_NODEFLIB))
+    add_path (&rtld_search_dirs, XXX_default);
+
+  if (counting)
+    /* Count the struct size before the string area, which we didn't
+       know before we completed dls_cnt.  */
+    si->dls_size += (char *) &si->dls_serpath[si->dls_cnt] - (char *) si;
+}
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 2d8041fa68..8bd2efb8f0 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -554,6 +554,16 @@ extern void _dl_setup_hash (struct link_map *map)
      internal_function attribute_hidden;
 
 
+/* Collect the directories in the search path for LOADER's dependencies.
+   The data structure is defined in <dlfcn.h>.  If COUNTING is true,
+   SI->dls_cnt and SI->dls_size are set; if false, those must be as set
+   by a previous call with COUNTING set, and SI must point to SI->dls_size
+   bytes to be used in filling in the result.  */
+extern void _dl_rtld_di_serinfo (struct link_map *loader,
+				 Dl_serinfo *si, bool counting)
+     internal_function;
+
+
 /* Search loaded objects' symbol tables for a definition of the symbol
    referred to by UNDEF.  *SYM is the symbol table entry containing the
    reference; it is replaced with the defining symbol, and the base load