GNU bug report logs - #44899
[PATCH 0/3] Using 'ld.so.cache' to speed up application startup

Previous Next

Package: guix-patches;

Reported by: Ludovic Courtès <ludo <at> gnu.org>

Date: Fri, 27 Nov 2020 08:34:02 UTC

Severity: normal

Tags: patch

Done: Ludovic Courtès <ludo <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 44899 in the body.
You can then email your comments to 44899 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Fri, 27 Nov 2020 08:34:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Ludovic Courtès <ludo <at> gnu.org>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Fri, 27 Nov 2020 08:34:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: guix-patches <at> gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 0/3] Using 'ld.so.cache' to speed up application startup
Date: Fri, 27 Nov 2020 09:33:02 +0100
Hello Guix!

The other day on IRC Ricardo had the brilliant idea of using the
ld.so cache to avoid the “stat storm” stemming from our long RUNPATHs,
and thus to speed up application startup.  As an example, Guile has
9 entries in its RUNPATH and Inkscape has 44 entries.

The first patch changes the loader (1) to look for the cache in
$ORIGIN/../etc/ld.so.cache, and (2) to look for the cache before
looking at RUNPATH entries.

The second patch adds a build phase that creates ‘etc/ld.so.cache’.
It passes ‘ldconfig’ a config file that contains the union of all
the RUNPATH entries of all the executables found in the output at
hand.  (It cannot be done in a profile hook because $ORIGIN is
determined by looking at /proc/self/exe, which is the canonical file
name in the store.)

You can see it in action with LD_DEBUG=libs:

--8<---------------cut here---------------start------------->8---
$ LD_DEBUG=libs /gnu/store/3dfv892jq8081xnsl9gfylbgv1fdicvd-guile-3.0.4/bin/guile --version
     11150:     find library=libguile-3.0.so.1 [0]; searching
     11150:      search cache=/gnu/store/3dfv892jq8081xnsl9gfylbgv1fdicvd-guile-3.0.4/bin/../etc/ld.so.cache
     11150:       trying file=/gnu/store/3dfv892jq8081xnsl9gfylbgv1fdicvd-guile-3.0.4/lib/libguile-3.0.so.1
     11150:
     11150:     find library=libgc.so.1 [0]; searching
     11150:      search cache=/gnu/store/3dfv892jq8081xnsl9gfylbgv1fdicvd-guile-3.0.4/bin/../etc/ld.so.cache
     11150:       trying file=/gnu/store/hy88vf2ynlica0wj0ppi0d3b11gi2b2h-libgc-8.0.4/lib/libgc.so.1

[...]
--8<---------------cut here---------------end--------------->8---

Here’s the after/before for Guile:

--8<---------------cut here---------------start------------->8---
$ strace -c /gnu/store/3dfv892jq8081xnsl9gfylbgv1fdicvd-guile-3.0.4/bin/guile --version

[...]

  2.70    0.000259           5        46         6 openat
  1.87    0.000180           1       130        88 stat
  1.66    0.000159           4        36           rt_sigprocmask
  0.74    0.000071           1        40           close
  0.64    0.000061           3        18           fstat

[...]

100.00    0.009604                   600       105 total
$ strace -c guile --version

[...]

 13.82    0.000723           4       165       114 openat
 13.46    0.000704           3       190       144 stat

[...]

  1.43    0.000075           2        29           fstat

[...]

100.00    0.005232                   773       268 total
--8<---------------cut here---------------end--------------->8---

Erroneous syscalls are divided by 2.5; total syscalls reduced by 22%.

For Bash:

--8<---------------cut here---------------start------------->8---
$ strace -c /gnu/store/qs33sf58502v1wx77va092y14sbspv4f-bash-minimal-5.0.16/bin/bash --version
GNU bash, version 5.0.16(1)-release (x86_64-unknown-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  0.00    0.000000           0         4           read
  0.00    0.000000           0         6           write
  0.00    0.000000           0         5           close
  0.00    0.000000           0         5           fstat
  0.00    0.000000           0        12           mmap
  0.00    0.000000           0         5           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           readlink
  0.00    0.000000           0         1           getuid
  0.00    0.000000           0         1           getgid
  0.00    0.000000           0         1           geteuid
  0.00    0.000000           0         1           getegid
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         9         4 openat
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    59         5 total
$ strace -c /gnu/store/fvhj74pghapbjvsvj27skvkra1by1965-bash-minimal-5.0.16/bin/bash --version
GNU bash, version 5.0.16(1)-release (x86_64-unknown-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 34.15    0.000420          12        35        16 openat
 18.29    0.000225           8        27           mmap
  8.13    0.000100          25         4           brk
  7.72    0.000095           6        14        14 stat
  7.24    0.000089          89         1         1 access
  6.18    0.000076           4        19           fstat
  5.45    0.000067           3        19           close
  4.47    0.000055           2        20           read
  3.82    0.000047           9         5           mprotect
  2.36    0.000029           4         6           write
  0.65    0.000008           8         1           execve
  0.41    0.000005           5         1           arch_prctl
  0.33    0.000004           4         1           getuid
  0.24    0.000003           3         1           rt_sigprocmask
  0.24    0.000003           3         1           getegid
  0.16    0.000002           2         1           getgid
  0.16    0.000002           2         1           geteuid
------ ----------- ----------- --------- --------- ----------------
100.00    0.001230                   157        31 total
--8<---------------cut here---------------end--------------->8---

Erroneous syscalls are divided by 6; total syscalls divided by 2.7.

As always, this is probably not that big a deal on warm-cache SSD,
but it probably makes a difference on a cold cache, on spinning
disks, and on network file systems.

* Possible improvements

The hard-coded ‘../etc/ld.so.cache’ means that it can only be used
with first-level sub-directories like bin/ and sbin/; it won’t be
used for libexec/guix/guile, for instance, which is a bummer.
Perhaps we should compute the ‘ld.so.cache’ file name “lexically”
instead.

We should also think hard about ways users could be tricked into
loading a malicious ‘ld.so.cache’.  That’s also another reason why
“lexical dot-dot” would be safer: we could ensure that only
pre-computed ‘ld.so.cache’ that live in the store are ever loaded.

The ‘ld.so.conf’ file passed to ‘ldconfig’ should ideally contains
the RUNPATH entries _recursively_, such that even indirect
dependencies can be found in cache.

Thoughts?

Ludo’.

Ludovic Courtès (3):
  gnu: glibc: Load ${ORIGIN}/../etc/ld.so.cache when available.
  gremlin: Fix typo in docstring.
  build-system/gnu: Add 'make-dynamic-linker-cache' phase.

 gnu/local.mk                              |   1 +
 gnu/packages/base.scm                     |  11 +-
 gnu/packages/patches/glibc-dl-cache.patch | 122 ++++++++++++++++++++++
 guix/build-system/gnu.scm                 |   4 +
 guix/build/gnu-build-system.scm           |  73 +++++++++++++
 guix/build/gremlin.scm                    |   2 +-
 6 files changed, 202 insertions(+), 11 deletions(-)
 create mode 100644 gnu/packages/patches/glibc-dl-cache.patch

-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Fri, 27 Nov 2020 09:06:01 GMT) Full text and rfc822 format available.

Message #8 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 1/3] gnu: glibc: Load ${ORIGIN}/../etc/ld.so.cache when
 available.
Date: Fri, 27 Nov 2020 10:05:21 +0100
* gnu/packages/patches/glibc-dl-cache.patch: New file.
* gnu/local.mk (dist_patch_DATA): Add it.
* gnu/packages/base.scm (glibc)[source]: Remove 'snippet' and 'modules'.
---
 gnu/local.mk                              |   1 +
 gnu/packages/base.scm                     |  11 +-
 gnu/packages/patches/glibc-dl-cache.patch | 122 ++++++++++++++++++++++
 3 files changed, 124 insertions(+), 10 deletions(-)
 create mode 100644 gnu/packages/patches/glibc-dl-cache.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index f9fed30a3f..82c3c608c6 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1051,6 +1051,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/glibc-bootstrap-system-2.2.5.patch 	\
   %D%/packages/patches/glibc-bootstrap-system-2.16.0.patch 	\
   %D%/packages/patches/glibc-bootstrap-system.patch		\
+  %D%/packages/patches/glibc-dl-cache.patch			\
   %D%/packages/patches/glibc-hidden-visibility-ldconfig.patch	\
   %D%/packages/patches/glibc-hurd-clock_gettime_monotonic.patch	\
   %D%/packages/patches/glibc-hurd-clock_t_centiseconds.patch	\
diff --git a/gnu/packages/base.scm b/gnu/packages/base.scm
index bd352319a1..f8f4ae37fd 100644
--- a/gnu/packages/base.scm
+++ b/gnu/packages/base.scm
@@ -675,17 +675,8 @@ the store.")
             (sha256
              (base32
               "0di848ibffrnwq7g2dvgqrnn4xqhj3h96csn69q4da51ymafl9qn"))
-            (snippet
-             ;; Disable 'ldconfig' and /etc/ld.so.cache.  The latter is
-             ;; required on LFS distros to avoid loading the distro's libc.so
-             ;; instead of ours.
-             '(begin
-                (substitute* "sysdeps/unix/sysv/linux/configure"
-                  (("use_ldconfig=yes")
-                   "use_ldconfig=no"))
-                #t))
-            (modules '((guix build utils)))
             (patches (search-patches "glibc-ldd-x86_64.patch"
+                                     "glibc-dl-cache.patch"
                                      "glibc-hidden-visibility-ldconfig.patch"
                                      "glibc-versioned-locpath.patch"
                                      "glibc-allow-kernel-2.6.32.patch"
diff --git a/gnu/packages/patches/glibc-dl-cache.patch b/gnu/packages/patches/glibc-dl-cache.patch
new file mode 100644
index 0000000000..9d578bb3d9
--- /dev/null
+++ b/gnu/packages/patches/glibc-dl-cache.patch
@@ -0,0 +1,122 @@
+Read the shared library cache relative to $ORIGIN instead of reading
+from /etc/ld.so.cache.  Also arrange so that this cache takes
+precedence over RUNPATH.
+
+diff --git a/elf/dl-cache.c b/elf/dl-cache.c
+index 93d185e788..6a2989bd4c 100644
+--- a/elf/dl-cache.c
++++ b/elf/dl-cache.c
+@@ -171,6 +171,33 @@ _dl_cache_libcmp (const char *p1, const char *p2)
+   return *p1 - *p2;
+ }
+ 
++/* Special value representing the lack of an ld.so cache.  */
++static const char ld_so_cache_lacking[] = "/ld.so cache is lacking";
++
++/* Return the per-application ld.so cache, relative to $ORIGIN, or NULL if
++   that fails for some reason.  Do not return the system-wide LD_SO_CACHE
++   since on a foreign distro it would contain invalid information.  */
++static const char *
++ld_so_cache (void)
++{
++  static const char *loader_cache;
++
++  if (loader_cache == NULL)
++    {
++      const char suffix[] = "/../etc/ld.so.cache";
++      const char *origin = _dl_get_origin ();
++
++      /* Note: We can't use 'malloc' because it can be interposed.  */
++      char *cache = alloca (strlen (origin) + sizeof suffix);
++
++      strcpy (cache, origin);
++      strcat (cache, suffix);
++
++      loader_cache = __strdup (cache) ?: ld_so_cache_lacking;
++    }
++
++  return loader_cache;
++}
+ 
+ /* Look up NAME in ld.so.cache and return the file name stored there, or null
+    if none is found.  The cache is loaded if it was not already.  If loading
+@@ -190,12 +217,15 @@ _dl_load_cache_lookup (const char *name)
+ 
+   /* Print a message if the loading of libs is traced.  */
+   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+-    _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
++    _dl_debug_printf (" search cache=%s\n", ld_so_cache ());
++
++  if (__glibc_unlikely (ld_so_cache () == ld_so_cache_lacking))
++    return NULL;
+ 
+   if (cache == NULL)
+     {
+       /* Read the contents of the file.  */
+-      void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
++      void *file = _dl_sysdep_read_whole_file (ld_so_cache (), &cachesize,
+ 					       PROT_READ);
+ 
+       /* We can handle three different cache file formats here:
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index f3201e7c14..a69aec3428 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -2152,28 +2152,6 @@ _dl_map_object (struct link_map *loader, const char *name,
+ 			loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
+ 			LA_SER_LIBPATH, &found_other_class);
+ 
+-      /* 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, mode,
+-			&loader->l_runpath_dirs, &realname, &fb, loader,
+-			LA_SER_RUNPATH, &found_other_class);
+-
+-      if (fd == -1)
+-        {
+-          realname = _dl_sysdep_open_object (name, namelen, &fd);
+-          if (realname != NULL)
+-            {
+-              fd = open_verify (realname, fd,
+-                                &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+-                                LA_SER_CONFIG, mode, &found_other_class,
+-                                false);
+-              if (fd == -1)
+-                free (realname);
+-            }
+-        }
+-
+ #ifdef USE_LDCONFIG
+       if (fd == -1
+ 	  && (__glibc_likely ((mode & __RTLD_SECURE) == 0)
+@@ -2232,6 +2210,28 @@ _dl_map_object (struct link_map *loader, const char *name,
+ 	}
+ #endif
+ 
++      /* 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, mode,
++			&loader->l_runpath_dirs, &realname, &fb, loader,
++			LA_SER_RUNPATH, &found_other_class);
++
++      if (fd == -1)
++        {
++          realname = _dl_sysdep_open_object (name, namelen, &fd);
++          if (realname != NULL)
++            {
++              fd = open_verify (realname, fd,
++                                &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
++                                LA_SER_CONFIG, mode, &found_other_class,
++                                false);
++              if (fd == -1)
++                free (realname);
++            }
++        }
++
+       /* Finally, try the default path.  */
+       if (fd == -1
+ 	  && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Fri, 27 Nov 2020 09:06:02 GMT) Full text and rfc822 format available.

Message #11 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 2/3] gremlin: Fix typo in docstring.
Date: Fri, 27 Nov 2020 10:05:22 +0100
* guix/build/gremlin.scm (file-runpath): Fix typo.
---
 guix/build/gremlin.scm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/guix/build/gremlin.scm b/guix/build/gremlin.scm
index 6857e47b99..44604827a9 100644
--- a/guix/build/gremlin.scm
+++ b/guix/build/gremlin.scm
@@ -250,7 +250,7 @@ info."
       (elf-dynamic-info (parse-elf (get-bytevector-all port))))))
 
 (define (file-runpath file)
-  "Return the DT_RUNPATH dynamic entry of FILE as a list of string, or #f if
+  "Return the DT_RUNPATH dynamic entry of FILE as a list of strings, or #f if
 FILE lacks dynamic info."
   (and=> (file-dynamic-info file) elf-dynamic-info-runpath))
 
-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Fri, 27 Nov 2020 09:06:02 GMT) Full text and rfc822 format available.

Message #14 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH 3/3] build-system/gnu: Add 'make-dynamic-linker-cache' phase.
Date: Fri, 27 Nov 2020 10:05:23 +0100
* guix/build/gnu-build-system.scm (make-dynamic-linker-cache): New
procedure.
(%standard-phases): Add it.
* guix/build-system/gnu.scm (gnu-build, gnu-cross-build): Add
 #:make-dynamic-linker-cache? and honor it.
---
 guix/build-system/gnu.scm       |  4 ++
 guix/build/gnu-build-system.scm | 73 +++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)

diff --git a/guix/build-system/gnu.scm b/guix/build-system/gnu.scm
index 2c23197e77..d6c4dc9bbc 100644
--- a/guix/build-system/gnu.scm
+++ b/guix/build-system/gnu.scm
@@ -342,6 +342,7 @@ standard packages used as implicit inputs of the GNU build system."
                     (strip-directories ''("lib" "lib64" "libexec"
                                           "bin" "sbin"))
                     (validate-runpath? #t)
+                    (make-dynamic-linker-cache? #t)
                     (license-file-regexp %license-file-regexp)
                     (phases '%standard-phases)
                     (locale "en_US.utf8")
@@ -410,6 +411,7 @@ packages that must not be referenced."
                   #:patch-shebangs? ,patch-shebangs?
                   #:strip-binaries? ,strip-binaries?
                   #:validate-runpath? ,validate-runpath?
+                  #:make-dynamic-linker-cache? ,make-dynamic-linker-cache?
                   #:license-file-regexp ,license-file-regexp
                   #:strip-flags ,strip-flags
                   #:strip-directories ,strip-directories)))
@@ -497,6 +499,7 @@ is one of `host' or `target'."
                           (strip-directories ''("lib" "lib64" "libexec"
                                                 "bin" "sbin"))
                           (validate-runpath? #t)
+                          (make-dynamic-linker-cache? #t)
                           (license-file-regexp %license-file-regexp)
                           (phases '%standard-phases)
                           (locale "en_US.utf8")
@@ -577,6 +580,7 @@ platform."
                     #:patch-shebangs? ,patch-shebangs?
                     #:strip-binaries? ,strip-binaries?
                     #:validate-runpath? ,validate-runpath?
+                    #:make-dynamic-linker-cache? ,make-dynamic-linker-cache?
                     #:license-file-regexp ,license-file-regexp
                     #:strip-flags ,strip-flags
                     #:strip-directories ,strip-directories))))
diff --git a/guix/build/gnu-build-system.scm b/guix/build/gnu-build-system.scm
index 8fa11f4ea9..194cddc047 100644
--- a/guix/build/gnu-build-system.scm
+++ b/guix/build/gnu-build-system.scm
@@ -712,6 +712,78 @@ which cannot be found~%"
                                          (which binary) rest)))))))))
             outputs))
 
+(define* (make-dynamic-linker-cache #:key outputs
+                                    (make-dynamic-linker-cache? #t)
+                                    #:allow-other-keys)
+  "Create a dynamic linker cache under 'etc/ld.so.cache' in each of the
+OUTPUTS.  This reduces application startup time by avoiding the 'stat' storm
+that traversing all the RUNPATH entries entails."
+  (define (make-cache-for-output directory)
+    (define bin-directories
+      (filter-map (lambda (sub-directory)
+                    (let ((directory (string-append directory "/"
+                                                    sub-directory)))
+                      (and (directory-exists? directory)
+                           directory)))
+                  '("bin" "sbin")))
+
+    (define programs
+      ;; Programs that can benefit from the ld.so cache.  These programs must
+      ;; be in a directory such that:
+      ;;
+      ;;   (string-append (dirname PROGRAM) "../etc/ld.so.cache")
+      ;;
+      ;; potentially exists since that's what ld.so will look for.  Thus,
+      ;; something like 'libexec/foo/PROGRAM' is not a valid candidate.
+      (append-map (lambda (directory)
+                    (if (directory-exists? directory)
+                        (filter-map (lambda (file)
+                                      (let ((file (string-append
+                                                   directory "/" file)))
+                                        (and (executable-file? file)
+                                             (not (file-is-directory? file))
+                                             (elf-file? file)
+                                             file)))
+                                    (scandir directory))
+                        '()))
+                  bin-directories))
+
+    (define runpaths
+      ;; The union of RUNPATH entries.
+      (delete-duplicates
+       (append-map (lambda (program)
+                     (or (file-runpath program) '()))
+                   programs)))
+
+    (define cache-file
+      (string-append directory "/etc/ld.so.cache"))
+
+    (unless (null? runpaths)
+      (mkdir-p (dirname cache-file))
+      (guard (c ((invoke-error? c)
+                 ;; Do not treat 'ldconfig' failure as an error.
+                 (format (current-error-port)
+                         "warning: 'ldconfig' failed:~%")
+                 (report-invoke-error c (current-error-port))))
+        ;; Create a config file to tell 'ldconfig' where to look for the
+        ;; libraries that PROGRAMS need.
+        (call-with-output-file "/tmp/ld.so.conf"
+          (lambda (port)
+            (for-each (lambda (directory)
+                        (display directory port)
+                        (newline port))
+                      runpaths)))
+
+        (invoke "ldconfig" "-f" "/tmp/ld.so.conf" "-C" cache-file)
+        (format #t "created '~a' from ~a library search path entries~%"
+                cache-file (length runpaths)))))
+
+  (if make-dynamic-linker-cache?
+      (match outputs
+        (((_ . directories) ...)
+         (for-each make-cache-for-output directories)))
+      (format #t "ld.so cache not built~%")))
+
 (define %license-file-regexp
   ;; Regexp matching license files.
   "^(COPYING.*|LICEN[CS]E.*|[Ll]icen[cs]e.*|Copy[Rr]ight(\\.(txt|md))?)$")
@@ -791,6 +863,7 @@ which cannot be found~%"
             validate-documentation-location
             delete-info-dir-file
             patch-dot-desktop-files
+            make-dynamic-linker-cache
             install-license-files
             reset-gzip-timestamps
             compress-documentation)))
-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Sat, 28 Nov 2020 10:25:02 GMT) Full text and rfc822 format available.

Message #17 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH v2 1/4] gremlin: Fix typo in docstring.
Date: Sat, 28 Nov 2020 11:24:05 +0100
* guix/build/gremlin.scm (file-runpath): Fix typo.
---
 guix/build/gremlin.scm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/guix/build/gremlin.scm b/guix/build/gremlin.scm
index 6857e47b99..44604827a9 100644
--- a/guix/build/gremlin.scm
+++ b/guix/build/gremlin.scm
@@ -250,7 +250,7 @@ info."
       (elf-dynamic-info (parse-elf (get-bytevector-all port))))))
 
 (define (file-runpath file)
-  "Return the DT_RUNPATH dynamic entry of FILE as a list of string, or #f if
+  "Return the DT_RUNPATH dynamic entry of FILE as a list of strings, or #f if
 FILE lacks dynamic info."
   (and=> (file-dynamic-info file) elf-dynamic-info-runpath))
 
-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Sat, 28 Nov 2020 10:25:02 GMT) Full text and rfc822 format available.

Message #20 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH v2 2/4] gremlin: Add 'file-needed/recursive'.
Date: Sat, 28 Nov 2020 11:24:06 +0100
* guix/build/gremlin.scm (file-needed/recursive): New procedure.
* tests/gremlin.scm ("file-needed/recursive"): New test.
---
 guix/build/gremlin.scm | 41 +++++++++++++++++++++++++++++++++++++++++
 tests/gremlin.scm      | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)

diff --git a/guix/build/gremlin.scm b/guix/build/gremlin.scm
index 44604827a9..d56984b85e 100644
--- a/guix/build/gremlin.scm
+++ b/guix/build/gremlin.scm
@@ -44,6 +44,7 @@
             file-dynamic-info
             file-runpath
             file-needed
+            file-needed/recursive
 
             missing-runpath-error?
             missing-runpath-error-file
@@ -259,6 +260,46 @@ FILE lacks dynamic info."
 dynamic info."
   (and=> (file-dynamic-info file) elf-dynamic-info-needed))
 
+(define (file-needed/recursive file)
+  "Return two values: the list of absolute .so file names FILE depends on,
+recursively, and the list of .so file names that could not be found.  File
+names are resolved by searching the RUNPATH of the file that NEEDs them.
+
+This is similar to the info returned by the 'ldd' command."
+  (let loop ((files  (list file))
+             (result '())
+             (not-found '()))
+    (match files
+      (()
+       (values (reverse result)
+               (reverse (delete-duplicates not-found))))
+      ((file . rest)
+       (match (file-dynamic-info (pk 'file file))
+         (#f
+          (loop rest result not-found))
+         (info
+          (let ((runpath (elf-dynamic-info-runpath info))
+                (needed  (elf-dynamic-info-needed info)))
+            (if (and runpath needed)
+                (let* ((runpath  (map (cute expand-origin <> (dirname file))
+                                      runpath))
+                       (resolved (map (cut search-path runpath <>)
+                                      needed))
+                       (failed   (filter-map (lambda (needed resolved)
+                                               (and (not resolved)
+                                                    (not (libc-library? needed))
+                                                    needed))
+                                             needed resolved))
+                       (needed   (remove (lambda (value)
+                                           (or (not value)
+                                               ;; XXX: quadratic
+                                               (member value result)))
+                                         resolved)))
+                  (loop (append rest needed)
+                        (append needed result)
+                        (append failed not-found)))
+                (loop rest result not-found)))))))))
+
 (define %libc-libraries
   ;; List of libraries as of glibc 2.21 (there are more but those are
   ;; typically mean to be LD_PRELOADed and thus do not appear as NEEDED.)
diff --git a/tests/gremlin.scm b/tests/gremlin.scm
index f191adb8b3..9ddac14265 100644
--- a/tests/gremlin.scm
+++ b/tests/gremlin.scm
@@ -27,6 +27,8 @@
   #:use-module (srfi srfi-64)
   #:use-module (rnrs io ports)
   #:use-module (ice-9 popen)
+  #:use-module (ice-9 rdelim)
+  #:use-module (ice-9 regex)
   #:use-module (ice-9 match))
 
 (define %guile-executable
@@ -58,6 +60,40 @@
                        (string-take lib (string-contains lib ".so")))
                      (elf-dynamic-info-needed dyninfo))))))
 
+(unless (and %guile-executable (not (getenv "LD_LIBRARY_PATH"))
+             (file-needed %guile-executable))     ;statically linked?
+  (test-skip 1))
+(test-assert "file-needed/recursive"
+  (let* ((needed (file-needed/recursive %guile-executable))
+         (pipe   (dynamic-wind
+                   (lambda ()
+                     ;; Tell ld.so to list loaded objects, like 'ldd' does.
+                     (setenv "LD_TRACE_LOADED_OBJECTS" "yup"))
+                   (lambda ()
+                     (open-pipe* OPEN_READ %guile-executable))
+                   (lambda ()
+                     (unsetenv "LD_TRACE_LOADED_OBJECTS")))))
+    (define ldd-rx
+      (make-regexp "^[[:blank:]]+([[:graph:]]+ => )?([[:graph:]]+) .*$"))
+
+    (define (read-ldd-output port)
+      ;; Read from PORT output in GNU ldd format.
+      (let loop ((result '()))
+        (match (read-line port)
+          ((? eof-object?)
+           (reverse result))
+          ((= (cut regexp-exec ldd-rx <>) m)
+           (if m
+               (loop (cons (match:substring m 2) result))
+               (loop result))))))
+
+    (define ground-truth
+      (remove (cut string-prefix? "linux-vdso.so" <>)
+              (read-ldd-output pipe)))
+
+    (and (zero? (close-pipe pipe))
+         (lset= string=? (pk 'truth ground-truth) (pk 'needed needed)))))
+
 (test-equal "expand-origin"
   '("OOO/../lib"
     "OOO"
-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Sat, 28 Nov 2020 10:25:03 GMT) Full text and rfc822 format available.

Message #23 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH v2 4/4] build-system/gnu: Add 'make-dynamic-linker-cache'
 phase.
Date: Sat, 28 Nov 2020 11:24:09 +0100
* guix/build/gnu-build-system.scm (make-dynamic-linker-cache): New
procedure.
(%standard-phases): Add it.
* guix/build-system/gnu.scm (gnu-build, gnu-cross-build): Add
 #:make-dynamic-linker-cache? and honor it.
---
 guix/build-system/gnu.scm       |  4 ++
 guix/build/gnu-build-system.scm | 68 +++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)

diff --git a/guix/build-system/gnu.scm b/guix/build-system/gnu.scm
index 2c23197e77..d6c4dc9bbc 100644
--- a/guix/build-system/gnu.scm
+++ b/guix/build-system/gnu.scm
@@ -342,6 +342,7 @@ standard packages used as implicit inputs of the GNU build system."
                     (strip-directories ''("lib" "lib64" "libexec"
                                           "bin" "sbin"))
                     (validate-runpath? #t)
+                    (make-dynamic-linker-cache? #t)
                     (license-file-regexp %license-file-regexp)
                     (phases '%standard-phases)
                     (locale "en_US.utf8")
@@ -410,6 +411,7 @@ packages that must not be referenced."
                   #:patch-shebangs? ,patch-shebangs?
                   #:strip-binaries? ,strip-binaries?
                   #:validate-runpath? ,validate-runpath?
+                  #:make-dynamic-linker-cache? ,make-dynamic-linker-cache?
                   #:license-file-regexp ,license-file-regexp
                   #:strip-flags ,strip-flags
                   #:strip-directories ,strip-directories)))
@@ -497,6 +499,7 @@ is one of `host' or `target'."
                           (strip-directories ''("lib" "lib64" "libexec"
                                                 "bin" "sbin"))
                           (validate-runpath? #t)
+                          (make-dynamic-linker-cache? #t)
                           (license-file-regexp %license-file-regexp)
                           (phases '%standard-phases)
                           (locale "en_US.utf8")
@@ -577,6 +580,7 @@ platform."
                     #:patch-shebangs? ,patch-shebangs?
                     #:strip-binaries? ,strip-binaries?
                     #:validate-runpath? ,validate-runpath?
+                    #:make-dynamic-linker-cache? ,make-dynamic-linker-cache?
                     #:license-file-regexp ,license-file-regexp
                     #:strip-flags ,strip-flags
                     #:strip-directories ,strip-directories))))
diff --git a/guix/build/gnu-build-system.scm b/guix/build/gnu-build-system.scm
index 8fa11f4ea9..5f08b9d6ac 100644
--- a/guix/build/gnu-build-system.scm
+++ b/guix/build/gnu-build-system.scm
@@ -712,6 +712,73 @@ which cannot be found~%"
                                          (which binary) rest)))))))))
             outputs))
 
+(define* (make-dynamic-linker-cache #:key outputs
+                                    (make-dynamic-linker-cache? #t)
+                                    #:allow-other-keys)
+  "Create a dynamic linker cache under 'etc/ld.so.cache' in each of the
+OUTPUTS.  This reduces application startup time by avoiding the 'stat' storm
+that traversing all the RUNPATH entries entails."
+  (define (make-cache-for-output directory)
+    (define bin-directories
+      (filter-map (lambda (sub-directory)
+                    (let ((directory (string-append directory "/"
+                                                    sub-directory)))
+                      (and (directory-exists? directory)
+                           directory)))
+                  '("bin" "sbin" "libexec")))
+
+    (define programs
+      ;; Programs that can benefit from the ld.so cache.
+      (append-map (lambda (directory)
+                    (if (directory-exists? directory)
+                        (find-files directory
+                                    (lambda (file stat)
+                                      (and (executable-file? file)
+                                           (elf-file? file))))
+                        '()))
+                  bin-directories))
+
+    (define library-path
+      ;; Directories containing libraries that PROGRAMS depend on,
+      ;; recursively.
+      (delete-duplicates
+       (append-map (lambda (program)
+                     (map dirname (file-needed/recursive program)))
+                   programs)))
+
+    (define cache-file
+      (string-append directory "/etc/ld.so.cache"))
+
+    (define ld.so.conf
+      (string-append (or (getenv "TMPDIR") "/tmp")
+                     "/ld.so.conf"))
+
+    (unless (null? library-path)
+      (mkdir-p (dirname cache-file))
+      (guard (c ((invoke-error? c)
+                 ;; Do not treat 'ldconfig' failure as an error.
+                 (format (current-error-port)
+                         "warning: 'ldconfig' failed:~%")
+                 (report-invoke-error c (current-error-port))))
+        ;; Create a config file to tell 'ldconfig' where to look for the
+        ;; libraries that PROGRAMS need.
+        (call-with-output-file ld.so.conf
+          (lambda (port)
+            (for-each (lambda (directory)
+                        (display directory port)
+                        (newline port))
+                      library-path)))
+
+        (invoke "ldconfig" "-f" ld.so.conf "-C" cache-file)
+        (format #t "created '~a' from ~a library search path entries~%"
+                cache-file (length library-path)))))
+
+  (if make-dynamic-linker-cache?
+      (match outputs
+        (((_ . directories) ...)
+         (for-each make-cache-for-output directories)))
+      (format #t "ld.so cache not built~%")))
+
 (define %license-file-regexp
   ;; Regexp matching license files.
   "^(COPYING.*|LICEN[CS]E.*|[Ll]icen[cs]e.*|Copy[Rr]ight(\\.(txt|md))?)$")
@@ -791,6 +858,7 @@ which cannot be found~%"
             validate-documentation-location
             delete-info-dir-file
             patch-dot-desktop-files
+            make-dynamic-linker-cache
             install-license-files
             reset-gzip-timestamps
             compress-documentation)))
-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Sat, 28 Nov 2020 10:25:03 GMT) Full text and rfc822 format available.

Message #26 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH v2 0/4] Using 'ld.so.cache' to speed up application startup
Date: Sat, 28 Nov 2020 11:24:04 +0100
Hi!

This new version addresses the shortcomings I mentioned earlier and
other issues reported on IRC:

  • ld.so no longer uses “../etc/ld.so.cache”.  Instead, it (1) ensures
    $ORIGIN is in the store, (2) extracts the store file name, and
    (3) appends “/etc/ld.so.cache”.  IOW, the ‘ld.so.cache’ is always
    resolved relative to the store directory $ORIGIN belongs to, not
    relative to $ORIGIN itself.

    Thinking about it, it’s a direct translation of the ld.so.cache model
    from FHS to the functional model where a system-wide /etc/ld.so.cache
    makes no sense.

  • ‘make-dynamic-linker-cache’ now creates an ‘ld.so.conf’ that contains
    all the dependencies, recursively, as would be returned by ‘ldd’.
    The new ‘file-needed/recursive’ procedure returns the list of shared
    objects depended on like ‘ldd’.

  • ‘make-dynamic-linker-cache’ creates ‘ld.so.conf’ in $TMPDIR rather
    than hard-code /tmp, so as to be friendlier to ‘--disable-chroot’
    builds (as is currently used on GNU/Hurd).

I’m rather happy and confident with that version.  :-)

Feedback welcome!

Ludo’.

Ludovic Courtès (4):
  gremlin: Fix typo in docstring.
  gremlin: Add 'file-needed/recursive'.
  gnu: glibc: Load 'etc/ld.so.cache' in $ORIGIN's store item when
    available.
  build-system/gnu: Add 'make-dynamic-linker-cache' phase.

 gnu/local.mk                              |   1 +
 gnu/packages/base.scm                     |  16 +--
 gnu/packages/patches/glibc-dl-cache.patch | 140 ++++++++++++++++++++++
 guix/build-system/gnu.scm                 |   4 +
 guix/build/gnu-build-system.scm           |  68 +++++++++++
 guix/build/gremlin.scm                    |  43 ++++++-
 tests/gremlin.scm                         |  36 ++++++
 7 files changed, 297 insertions(+), 11 deletions(-)
 create mode 100644 gnu/packages/patches/glibc-dl-cache.patch

-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Sat, 28 Nov 2020 10:25:03 GMT) Full text and rfc822 format available.

Message #29 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH v2 3/4] gnu: glibc: Load 'etc/ld.so.cache' in $ORIGIN's store
 item when available.
Date: Sat, 28 Nov 2020 11:24:07 +0100
* gnu/packages/patches/glibc-dl-cache.patch: New file.
* gnu/local.mk (dist_patch_DATA): Add it.
* gnu/packages/base.scm (glibc)[source]: Remove 'snippet' and 'modules'.
[arguments]: In 'pre-configure' phase, substitute @STORE_DIRECTORY@ in
'elf/dl-cache.c'.
---
 gnu/local.mk                              |   1 +
 gnu/packages/base.scm                     |  16 +--
 gnu/packages/patches/glibc-dl-cache.patch | 140 ++++++++++++++++++++++
 3 files changed, 147 insertions(+), 10 deletions(-)
 create mode 100644 gnu/packages/patches/glibc-dl-cache.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index f9fed30a3f..82c3c608c6 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1051,6 +1051,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/glibc-bootstrap-system-2.2.5.patch 	\
   %D%/packages/patches/glibc-bootstrap-system-2.16.0.patch 	\
   %D%/packages/patches/glibc-bootstrap-system.patch		\
+  %D%/packages/patches/glibc-dl-cache.patch			\
   %D%/packages/patches/glibc-hidden-visibility-ldconfig.patch	\
   %D%/packages/patches/glibc-hurd-clock_gettime_monotonic.patch	\
   %D%/packages/patches/glibc-hurd-clock_t_centiseconds.patch	\
diff --git a/gnu/packages/base.scm b/gnu/packages/base.scm
index bd352319a1..ad4415f226 100644
--- a/gnu/packages/base.scm
+++ b/gnu/packages/base.scm
@@ -675,17 +675,8 @@ the store.")
             (sha256
              (base32
               "0di848ibffrnwq7g2dvgqrnn4xqhj3h96csn69q4da51ymafl9qn"))
-            (snippet
-             ;; Disable 'ldconfig' and /etc/ld.so.cache.  The latter is
-             ;; required on LFS distros to avoid loading the distro's libc.so
-             ;; instead of ours.
-             '(begin
-                (substitute* "sysdeps/unix/sysv/linux/configure"
-                  (("use_ldconfig=yes")
-                   "use_ldconfig=no"))
-                #t))
-            (modules '((guix build utils)))
             (patches (search-patches "glibc-ldd-x86_64.patch"
+                                     "glibc-dl-cache.patch"
                                      "glibc-hidden-visibility-ldconfig.patch"
                                      "glibc-versioned-locpath.patch"
                                      "glibc-allow-kernel-2.6.32.patch"
@@ -800,6 +791,11 @@ the store.")
                         ;; 4.7.1.
                         ((" -lgcc_s") ""))
 
+                      ;; Tell the ld.so cache code where the store is.
+                      (substitute* "elf/dl-cache.c"
+                        (("@STORE_DIRECTORY@")
+                         (string-append "\"" (%store-directory) "\"")))
+
                       ;; Have `system' use that Bash.
                       (substitute* "sysdeps/posix/system.c"
                         (("#define[[:blank:]]+SHELL_PATH.*$")
diff --git a/gnu/packages/patches/glibc-dl-cache.patch b/gnu/packages/patches/glibc-dl-cache.patch
new file mode 100644
index 0000000000..0f23b12add
--- /dev/null
+++ b/gnu/packages/patches/glibc-dl-cache.patch
@@ -0,0 +1,140 @@
+Read the shared library cache relative to $ORIGIN instead of reading
+from /etc/ld.so.cache.  Also arrange so that this cache takes
+precedence over RUNPATH.
+
+diff --git a/elf/dl-cache.c b/elf/dl-cache.c
+index 93d185e788..e0760a1f40 100644
+--- a/elf/dl-cache.c
++++ b/elf/dl-cache.c
+@@ -171,6 +171,51 @@ _dl_cache_libcmp (const char *p1, const char *p2)
+   return *p1 - *p2;
+ }
+ 
++/* Special value representing the lack of an ld.so cache.  */
++static const char ld_so_cache_lacking[] = "/ld.so cache is lacking";
++
++/* Return the per-application ld.so cache, relative to $ORIGIN, or NULL if
++   that fails for some reason.  Do not return the system-wide LD_SO_CACHE
++   since on a foreign distro it would contain invalid information.  */
++static const char *
++ld_so_cache (void)
++{
++  static const char *loader_cache;
++
++  if (loader_cache == NULL)
++    {
++      static const char store[] = "/gnu/store";
++      const char *origin = _dl_get_origin ();
++
++      /* Check whether ORIGIN is something like "/gnu/store/…-foo/bin".  */
++      if (strncmp (store, origin, strlen (store)) == 0
++	  && origin[sizeof store - 1] == '/')
++	{
++	  char *store_item_end = strchr (origin + sizeof store, '/');
++
++	  if (store_item_end != NULL)
++	    {
++	      static const char suffix[] = "/etc/ld.so.cache";
++	      size_t store_item_len = store_item_end - origin;
++
++	      /* Note: We can't use 'malloc' because it can be interposed.
++		 Likewise, 'strncpy' is not available.  */
++	      char *cache = alloca (strlen (origin) + sizeof suffix);
++
++	      strcpy (cache, origin);
++	      strcpy (cache + store_item_len, suffix);
++
++	      loader_cache = __strdup (cache) ?: ld_so_cache_lacking;
++	    }
++	  else
++	    loader_cache = ld_so_cache_lacking;
++	}
++      else
++	loader_cache = ld_so_cache_lacking;
++    }
++
++  return loader_cache;
++}
+ 
+ /* Look up NAME in ld.so.cache and return the file name stored there, or null
+    if none is found.  The cache is loaded if it was not already.  If loading
+@@ -190,12 +235,15 @@ _dl_load_cache_lookup (const char *name)
+ 
+   /* Print a message if the loading of libs is traced.  */
+   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+-    _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
++    _dl_debug_printf (" search cache=%s\n", ld_so_cache ());
++
++  if (__glibc_unlikely (ld_so_cache () == ld_so_cache_lacking))
++    return NULL;
+ 
+   if (cache == NULL)
+     {
+       /* Read the contents of the file.  */
+-      void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
++      void *file = _dl_sysdep_read_whole_file (ld_so_cache (), &cachesize,
+ 					       PROT_READ);
+ 
+       /* We can handle three different cache file formats here:
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index f3201e7c14..a69aec3428 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -2152,28 +2152,6 @@ _dl_map_object (struct link_map *loader, const char *name,
+ 			loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
+ 			LA_SER_LIBPATH, &found_other_class);
+ 
+-      /* 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, mode,
+-			&loader->l_runpath_dirs, &realname, &fb, loader,
+-			LA_SER_RUNPATH, &found_other_class);
+-
+-      if (fd == -1)
+-        {
+-          realname = _dl_sysdep_open_object (name, namelen, &fd);
+-          if (realname != NULL)
+-            {
+-              fd = open_verify (realname, fd,
+-                                &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+-                                LA_SER_CONFIG, mode, &found_other_class,
+-                                false);
+-              if (fd == -1)
+-                free (realname);
+-            }
+-        }
+-
+ #ifdef USE_LDCONFIG
+       if (fd == -1
+ 	  && (__glibc_likely ((mode & __RTLD_SECURE) == 0)
+@@ -2232,6 +2210,28 @@ _dl_map_object (struct link_map *loader, const char *name,
+ 	}
+ #endif
+ 
++      /* 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, mode,
++			&loader->l_runpath_dirs, &realname, &fb, loader,
++			LA_SER_RUNPATH, &found_other_class);
++
++      if (fd == -1)
++        {
++          realname = _dl_sysdep_open_object (name, namelen, &fd);
++          if (realname != NULL)
++            {
++              fd = open_verify (realname, fd,
++                                &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
++                                LA_SER_CONFIG, mode, &found_other_class,
++                                false);
++              if (fd == -1)
++                free (realname);
++            }
++        }
++
+       /* Finally, try the default path.  */
+       if (fd == -1
+ 	  && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
-- 
2.29.2





Information forwarded to guix-patches <at> gnu.org:
bug#44899; Package guix-patches. (Sat, 28 Nov 2020 10:25:04 GMT) Full text and rfc822 format available.

Message #32 received at 44899 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899 <at> debbugs.gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH v2 3/4] gnu: glibc: Load ${ORIGIN}/../etc/ld.so.cache when
 available.
Date: Sat, 28 Nov 2020 11:24:08 +0100
* gnu/packages/patches/glibc-dl-cache.patch: New file.
* gnu/local.mk (dist_patch_DATA): Add it.
* gnu/packages/base.scm (glibc)[source]: Remove 'snippet' and 'modules'.
[arguments]: In 'pre-configure' phase, substitute @STORE_DIRECTORY@ in
'elf/dl-cache.c'.
---
 gnu/local.mk                              |   1 +
 gnu/packages/base.scm                     |  16 +--
 gnu/packages/patches/glibc-dl-cache.patch | 140 ++++++++++++++++++++++
 3 files changed, 147 insertions(+), 10 deletions(-)
 create mode 100644 gnu/packages/patches/glibc-dl-cache.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index f9fed30a3f..82c3c608c6 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1051,6 +1051,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/glibc-bootstrap-system-2.2.5.patch 	\
   %D%/packages/patches/glibc-bootstrap-system-2.16.0.patch 	\
   %D%/packages/patches/glibc-bootstrap-system.patch		\
+  %D%/packages/patches/glibc-dl-cache.patch			\
   %D%/packages/patches/glibc-hidden-visibility-ldconfig.patch	\
   %D%/packages/patches/glibc-hurd-clock_gettime_monotonic.patch	\
   %D%/packages/patches/glibc-hurd-clock_t_centiseconds.patch	\
diff --git a/gnu/packages/base.scm b/gnu/packages/base.scm
index bd352319a1..ad4415f226 100644
--- a/gnu/packages/base.scm
+++ b/gnu/packages/base.scm
@@ -675,17 +675,8 @@ the store.")
             (sha256
              (base32
               "0di848ibffrnwq7g2dvgqrnn4xqhj3h96csn69q4da51ymafl9qn"))
-            (snippet
-             ;; Disable 'ldconfig' and /etc/ld.so.cache.  The latter is
-             ;; required on LFS distros to avoid loading the distro's libc.so
-             ;; instead of ours.
-             '(begin
-                (substitute* "sysdeps/unix/sysv/linux/configure"
-                  (("use_ldconfig=yes")
-                   "use_ldconfig=no"))
-                #t))
-            (modules '((guix build utils)))
             (patches (search-patches "glibc-ldd-x86_64.patch"
+                                     "glibc-dl-cache.patch"
                                      "glibc-hidden-visibility-ldconfig.patch"
                                      "glibc-versioned-locpath.patch"
                                      "glibc-allow-kernel-2.6.32.patch"
@@ -800,6 +791,11 @@ the store.")
                         ;; 4.7.1.
                         ((" -lgcc_s") ""))
 
+                      ;; Tell the ld.so cache code where the store is.
+                      (substitute* "elf/dl-cache.c"
+                        (("@STORE_DIRECTORY@")
+                         (string-append "\"" (%store-directory) "\"")))
+
                       ;; Have `system' use that Bash.
                       (substitute* "sysdeps/posix/system.c"
                         (("#define[[:blank:]]+SHELL_PATH.*$")
diff --git a/gnu/packages/patches/glibc-dl-cache.patch b/gnu/packages/patches/glibc-dl-cache.patch
new file mode 100644
index 0000000000..0f23b12add
--- /dev/null
+++ b/gnu/packages/patches/glibc-dl-cache.patch
@@ -0,0 +1,140 @@
+Read the shared library cache relative to $ORIGIN instead of reading
+from /etc/ld.so.cache.  Also arrange so that this cache takes
+precedence over RUNPATH.
+
+diff --git a/elf/dl-cache.c b/elf/dl-cache.c
+index 93d185e788..e0760a1f40 100644
+--- a/elf/dl-cache.c
++++ b/elf/dl-cache.c
+@@ -171,6 +171,51 @@ _dl_cache_libcmp (const char *p1, const char *p2)
+   return *p1 - *p2;
+ }
+ 
++/* Special value representing the lack of an ld.so cache.  */
++static const char ld_so_cache_lacking[] = "/ld.so cache is lacking";
++
++/* Return the per-application ld.so cache, relative to $ORIGIN, or NULL if
++   that fails for some reason.  Do not return the system-wide LD_SO_CACHE
++   since on a foreign distro it would contain invalid information.  */
++static const char *
++ld_so_cache (void)
++{
++  static const char *loader_cache;
++
++  if (loader_cache == NULL)
++    {
++      static const char store[] = "/gnu/store";
++      const char *origin = _dl_get_origin ();
++
++      /* Check whether ORIGIN is something like "/gnu/store/…-foo/bin".  */
++      if (strncmp (store, origin, strlen (store)) == 0
++	  && origin[sizeof store - 1] == '/')
++	{
++	  char *store_item_end = strchr (origin + sizeof store, '/');
++
++	  if (store_item_end != NULL)
++	    {
++	      static const char suffix[] = "/etc/ld.so.cache";
++	      size_t store_item_len = store_item_end - origin;
++
++	      /* Note: We can't use 'malloc' because it can be interposed.
++		 Likewise, 'strncpy' is not available.  */
++	      char *cache = alloca (strlen (origin) + sizeof suffix);
++
++	      strcpy (cache, origin);
++	      strcpy (cache + store_item_len, suffix);
++
++	      loader_cache = __strdup (cache) ?: ld_so_cache_lacking;
++	    }
++	  else
++	    loader_cache = ld_so_cache_lacking;
++	}
++      else
++	loader_cache = ld_so_cache_lacking;
++    }
++
++  return loader_cache;
++}
+ 
+ /* Look up NAME in ld.so.cache and return the file name stored there, or null
+    if none is found.  The cache is loaded if it was not already.  If loading
+@@ -190,12 +235,15 @@ _dl_load_cache_lookup (const char *name)
+ 
+   /* Print a message if the loading of libs is traced.  */
+   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
+-    _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
++    _dl_debug_printf (" search cache=%s\n", ld_so_cache ());
++
++  if (__glibc_unlikely (ld_so_cache () == ld_so_cache_lacking))
++    return NULL;
+ 
+   if (cache == NULL)
+     {
+       /* Read the contents of the file.  */
+-      void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
++      void *file = _dl_sysdep_read_whole_file (ld_so_cache (), &cachesize,
+ 					       PROT_READ);
+ 
+       /* We can handle three different cache file formats here:
+diff --git a/elf/dl-load.c b/elf/dl-load.c
+index f3201e7c14..a69aec3428 100644
+--- a/elf/dl-load.c
++++ b/elf/dl-load.c
+@@ -2152,28 +2152,6 @@ _dl_map_object (struct link_map *loader, const char *name,
+ 			loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
+ 			LA_SER_LIBPATH, &found_other_class);
+ 
+-      /* 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, mode,
+-			&loader->l_runpath_dirs, &realname, &fb, loader,
+-			LA_SER_RUNPATH, &found_other_class);
+-
+-      if (fd == -1)
+-        {
+-          realname = _dl_sysdep_open_object (name, namelen, &fd);
+-          if (realname != NULL)
+-            {
+-              fd = open_verify (realname, fd,
+-                                &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+-                                LA_SER_CONFIG, mode, &found_other_class,
+-                                false);
+-              if (fd == -1)
+-                free (realname);
+-            }
+-        }
+-
+ #ifdef USE_LDCONFIG
+       if (fd == -1
+ 	  && (__glibc_likely ((mode & __RTLD_SECURE) == 0)
+@@ -2232,6 +2210,28 @@ _dl_map_object (struct link_map *loader, const char *name,
+ 	}
+ #endif
+ 
++      /* 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, mode,
++			&loader->l_runpath_dirs, &realname, &fb, loader,
++			LA_SER_RUNPATH, &found_other_class);
++
++      if (fd == -1)
++        {
++          realname = _dl_sysdep_open_object (name, namelen, &fd);
++          if (realname != NULL)
++            {
++              fd = open_verify (realname, fd,
++                                &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
++                                LA_SER_CONFIG, mode, &found_other_class,
++                                false);
++              if (fd == -1)
++                free (realname);
++            }
++        }
++
+       /* Finally, try the default path.  */
+       if (fd == -1
+ 	  && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
-- 
2.29.2





Reply sent to Ludovic Courtès <ludo <at> gnu.org>:
You have taken responsibility. (Tue, 01 Dec 2020 20:46:01 GMT) Full text and rfc822 format available.

Notification sent to Ludovic Courtès <ludo <at> gnu.org>:
bug acknowledged by developer. (Tue, 01 Dec 2020 20:46:01 GMT) Full text and rfc822 format available.

Message #37 received at 44899-done <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 44899-done <at> debbugs.gnu.org
Subject: Re: [bug#44899] [PATCH v2 0/4] Using 'ld.so.cache' to speed up
 application startup
Date: Tue, 01 Dec 2020 21:45:24 +0100
Ludovic Courtès <ludo <at> gnu.org> skribis:

>   gremlin: Fix typo in docstring.
>   gremlin: Add 'file-needed/recursive'.
>   gnu: glibc: Load 'etc/ld.so.cache' in $ORIGIN's store item when
>     available.
>   build-system/gnu: Add 'make-dynamic-linker-cache' phase.

Pushed as f85efa86e7690d9181946351631e02b1c20958c9, sans hard-coded
/gnu/store in the patch as reported by Ricardo on IRC and debugging
leftover in gremlin.scm.

Enjoy… and report any issues you may find!  :-)

Ludo’.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Wed, 30 Dec 2020 12:24:08 GMT) Full text and rfc822 format available.

This bug report was last modified 4 years and 169 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.