[glibc] elf: Avoid redundant ld.so.cache reload after first load

Adhemerval Zanella via Glibc-cvs <[email protected]> Mon, 13 Jul 2026 16:56:16 +0000 (GMT)
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=f1c94392d96ba10b6ea3acbd617f73d5fab35b88

commit f1c94392d96ba10b6ea3acbd617f73d5fab35b88
Author: Adhemerval Zanella <[email protected]>
Date:   Mon Jul 6 11:48:23 2026 -0300

    elf: Avoid redundant ld.so.cache reload after first load
    
    _dl_check_ldsocache_needs_loading only stored the stat fields it
    compares (mtime, ino, size, dev) on the path where a cache was already
    loaded.  On the very first call CACHE is NULL and the function returned
    "needs loading" without recording those fields, leaving
    new_cache_file_time zero.  The next call then copied that zero value
    into cache_file_time and compared it against the freshly stat'd values,
    which always differed, forcing a second, unnecessary load (munmap +
    mmap + re-parse) of an unchanged cache at every startup.
    
    It can be shown with repro:
    
      $ cat << EOF > repro.c
      #include <dlfcn.h>
      int main (void) { dlopen ("does-not-exist-xyz.so.99", RTLD_NOW); return 0; }
      EOF
      $ gcc repro.c -o repro
      $ strace -f -e trace=openat elf/ld.so --library-path . ./repro 2>&1 | grep -c "/etc/ld.so.cache"
    
    The result should be 1, instead of 2.
    
    Record the stat fields as soon as the stat succeeds, before the
    CACHE == NULL early return, so the following call has an accurate
    baseline and does not spuriously reload.
    
    Reviewed-by: DJ Delorie <[email protected]>

Diff:
---
 elf/dl-cache.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index 498196d0aa..03bcd5bd06 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -433,17 +433,20 @@ _dl_check_ldsocache_needs_loading (void)
   if (rv < 0)
     return false;
 
-  /* Any file is better than no file (likely the first time
-     through).  */
-  if (cache == NULL)
-    return true;
-
-  /* Store the fields we check, in order they're likely to differ.  */
+  /* Store the fields we check, in order they're likely to differ.  We
+     must do this even for the first load (CACHE == NULL below), so that
+     the next call copies an accurate NEW_CACHE_FILE_TIME into
+     CACHE_FILE_TIME and does not spuriously reload the unchanged cache.  */
   new_cache_file_time.mtime = new_cache_file_stat.st_mtime;
   new_cache_file_time.ino = new_cache_file_stat.st_ino;
   new_cache_file_time.size = new_cache_file_stat.st_size;
   new_cache_file_time.dev = new_cache_file_stat.st_dev;
 
+  /* Any file is better than no file (likely the first time
+     through).  */
+  if (cache == NULL)
+    return true;
+
   /* At this point, NEW_CACHE_FILE_TIME is valid as well as
      CACHE_FILE_TIME, so we compare them.  */
   return (memcmp (&new_cache_file_time, &cache_file_time,