[PATCH v7 0/4] elf: Release dl_load_lock around ELF constructors and destructors + LD_DEBUG=loadlock observability (BZ 15686)

[email protected] Mon, 3 Aug 2026 23:03:22 +0300
Newsgroups gmane.comp.lib.glibc.alpha
Message-ID <[email protected]>
From: Artem Proskurnev <[email protected]>

This v6 series implements the three concrete asks from Carlos's
2026-07-21 review of v3 ("Re: [PATCH v3] elf: Release dl_load_lock
before running dlopen constructors (BZ 15686)"):

  (a) Keep the glibc.rtld.strict_init_order tunable, document it, and
      flag it for eventual removal once the ecosystem has been shown
      to work with the new default.  Carlos on (3) in that message:
      "I think it's OK to keep the tunable ... The documentation
      should clearly state we intend to remove this when the
      ecosystem has been shown to work with the new implementation."

  (b) Extend the same dl_load_lock release to ELF destructors so the
      dlclose path mirrors the dlopen path.  Carlos on (4): "I'm also
      concerned about destructors since both see this kind of problem.
      What prevents us from providing the same invariant in
      destructors?"

  (c) Add an LD_DEBUG option that logs every dl_load_lock acquisition
      and release site with a raw-address backtrace, so residual
      deadlocks of this shape can be localised without a debugger.
      Carlos on tooling: "I think we should add this as a 2/2 patch,
      which improves the observability."  Then in reply to my
      question about how admins know to flip the tunable: "How does
      a system admin, or developer know to flip the tunable?" -- the
      LD_DEBUG=loadlock trace is the answer.

Patch layout:

  0001 - Release dl_load_lock before running dlopen constructors
         (BZ 15686).  Constructor side.  Adds the
         glibc.rtld.strict_init_order tunable (documented in
         manual/tunables.texi, marked for eventual removal), the
         l_init_once / l_init_owner / l_init_pending fields on
         struct link_map, and tst-create2..6 covering the ctor
         deadlock and concurrent-dlopen races.  tst-create4 was
         reworked in v7: the old test (flaky probabilistic interleave
         check, marked XFAIL) is replaced by a deterministic
         barrier-based test that verifies a long-running constructor
         does not block concurrent dlopen of an unrelated library --
         the core invariant the BZ 15686 fix provides.

  0002 - Release dl_load_lock before running dlclose destructors
         (BZ 15686).  Destructor side; same tunable, same caveat.
         Mirrors the _dl_fini behaviour on the _dl_close_worker
         path.  Adds tst-create7..10 covering the symmetric dtor
         scenarios.

  0003 - Add LD_DEBUG=loadlock to trace dl_load_lock acquisitions
         (BZ 15686).  Instruments the dlopen constructor path.  The
         backtrace is a manual frame-pointer walk -- deliberately
         lock-free, allocation-free and syscall-free, so the trace
         can be emitted while dl_load_lock is held without
         re-entering the loader.  Raw addresses are resolved offline
         with addr2line(1).

  0004 - Add LD_DEBUG=loadlock tracing for the dlclose destructor
         path (BZ 15686).  Extends 0003 coverage to _dl_close and
         _dl_close_worker, including the BZ 15686 release site
         around _dl_call_fini.

The scope claim made in v3 (originally independent threads can run
independent dlopens concurrently; a constructor or destructor may
call any libc / dynamic-loader function safe to call from a regular
non-signal-handler thread) now applies symmetrically to constructors
and destructors.  Synchronous loader-internal reentrancy (e.g. a
malloc interposer that itself calls dlopen, routing through the
loader's own malloc during dlopen processing outside the ctor/dtor
window) remains out of scope, as in v3.

Testing: x86_64 make check across five trees (unpatched, +0001,
+0001+0002, +0001..+0003, +0001..+0004) with the same tst-create1..10
regression suite forward-ported onto each tree.  Each functional
patch converts exactly one tst-create* timeout-on-deadlock into a
PASS with no other transitions; 0003 and 0004 each add one PASS
(elf/tst-debug-loadlock) for the LD_DEBUG=loadlock test and are
otherwise pure diagnostics with no behavioural diff.  tst-create4
(barrier-based deterministic test) passes on all five trees
confirming the lock release is effective; no XFAIL marker needed.

The tunable is expected to remain in tree for roughly four release
cycles, per Carlos's guidance, and is removed once the ecosystem
has been shown to adapt.

Artem Proskurnev (4):
  elf: Release dl_load_lock before running dlopen constructors (BZ
    15686)
  elf: Release dl_load_lock before running dlclose destructors (BZ
    15686)
  elf: Add LD_DEBUG=loadlock to trace dl_load_lock acquisitions (BZ
    15686)
  elf: Add LD_DEBUG=loadlock tracing for the dlclose destructor path (BZ
    15686)

 elf/Makefile                       |   5 +
 elf/dl-close.c                     |  87 ++++++++++++++--
 elf/dl-debug.c                     |  42 ++++++++
 elf/dl-init.c                      |  99 +++++++++++++++++-
 elf/dl-open.c                      | 161 ++++++++++++++++++++++++++++-
 elf/dl-tunables.list               |   6 ++
 elf/rtld.c                         |   2 +
 elf/tst-debug-loadlock-mod.c       |   4 +
 elf/tst-debug-loadlock.c           | 131 +++++++++++++++++++++++
 elf/tst-rtld-list-tunables.exp     |   1 +
 include/link.h                     |  22 ++++
 manual/dynlink.texi                |  12 +++
 manual/tunables.texi               |  46 +++++++++
 sysdeps/generic/ldsodefs.h         |  12 +++
 sysdeps/pthread/Makefile           | 104 +++++++++++++++++++
 sysdeps/pthread/tst-create10.c     | 119 +++++++++++++++++++++
 sysdeps/pthread/tst-create10mod.c  |  59 +++++++++++
 sysdeps/pthread/tst-create2.c      |  67 ++++++++++++
 sysdeps/pthread/tst-create2mod.c   |  74 +++++++++++++
 sysdeps/pthread/tst-create3.c      | 112 ++++++++++++++++++++
 sysdeps/pthread/tst-create3.h      |  27 +++++
 sysdeps/pthread/tst-create3mod.c   |  68 ++++++++++++
 sysdeps/pthread/tst-create4.c      | 100 ++++++++++++++++++
 sysdeps/pthread/tst-create4.h      |  37 +++++++
 sysdeps/pthread/tst-create4mod-a.c |  30 ++++++
 sysdeps/pthread/tst-create4mod-b.c |  22 ++++
 sysdeps/pthread/tst-create5.c      |  63 +++++++++++
 sysdeps/pthread/tst-create6.c      | 111 ++++++++++++++++++++
 sysdeps/pthread/tst-create6.h      |  26 +++++
 sysdeps/pthread/tst-create6mod.c   |  72 +++++++++++++
 sysdeps/pthread/tst-create7.c      | 129 +++++++++++++++++++++++
 sysdeps/pthread/tst-create7mod-a.c |  46 +++++++++
 sysdeps/pthread/tst-create7mod-b.c |  26 +++++
 sysdeps/pthread/tst-create8.c      |  97 +++++++++++++++++
 sysdeps/pthread/tst-create8mod-a.c |  55 ++++++++++
 sysdeps/pthread/tst-create8mod-b.c |  27 +++++
 sysdeps/pthread/tst-create9.c      | 113 ++++++++++++++++++++
 sysdeps/pthread/tst-create9mod.c   |  37 +++++++
 38 files changed, 2239 insertions(+), 12 deletions(-)
 create mode 100644 elf/tst-debug-loadlock-mod.c
 create mode 100644 elf/tst-debug-loadlock.c
 create mode 100644 sysdeps/pthread/tst-create10.c
 create mode 100644 sysdeps/pthread/tst-create10mod.c
 create mode 100644 sysdeps/pthread/tst-create2.c
 create mode 100644 sysdeps/pthread/tst-create2mod.c
 create mode 100644 sysdeps/pthread/tst-create3.c
 create mode 100644 sysdeps/pthread/tst-create3.h
 create mode 100644 sysdeps/pthread/tst-create3mod.c
 create mode 100644 sysdeps/pthread/tst-create4.c
 create mode 100644 sysdeps/pthread/tst-create4.h
 create mode 100644 sysdeps/pthread/tst-create4mod-a.c
 create mode 100644 sysdeps/pthread/tst-create4mod-b.c
 create mode 100644 sysdeps/pthread/tst-create5.c
 create mode 100644 sysdeps/pthread/tst-create6.c
 create mode 100644 sysdeps/pthread/tst-create6.h
 create mode 100644 sysdeps/pthread/tst-create6mod.c
 create mode 100644 sysdeps/pthread/tst-create7.c
 create mode 100644 sysdeps/pthread/tst-create7mod-a.c
 create mode 100644 sysdeps/pthread/tst-create7mod-b.c
 create mode 100644 sysdeps/pthread/tst-create8.c
 create mode 100644 sysdeps/pthread/tst-create8mod-a.c
 create mode 100644 sysdeps/pthread/tst-create8mod-b.c
 create mode 100644 sysdeps/pthread/tst-create9.c
 create mode 100644 sysdeps/pthread/tst-create9mod.c

-- 
2.51.0