public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/2] minimal run-time audit support
@ 2023-02-21 15:33 Stas Sergeev
  2023-02-21 15:33 ` [PATCH 1/2] elf/dl-open: fix audit wrt RTLD_NOLOAD [BZ #30127] Stas Sergeev
  2023-02-21 15:33 ` [PATCH 2/2] dlfcn,elf: impl dlload_audit_module " Stas Sergeev
  0 siblings, 2 replies; 8+ messages in thread
From: Stas Sergeev @ 2023-02-21 15:33 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch-set introduces the bare minimum functionality for working
with audit modules at run-time.

Patch1 fixes the check in dlmopen() which avoids loading solibs into
the auditing namespace. But RTLD_NOLOAD was forgotten to check, and
with that flags nothing gets loaded into auditing namespace, so dlmopen()
should succeed and return the handle for auditing object.

Patch2 adds dlload_audit_module() function and a test-case for it.

Stas Sergeev (2):
  elf/dl-open: fix audit wrt RTLD_NOLOAD [BZ #30127]
  dlfcn,elf: impl dlload_audit_module [BZ #30127]

 dlfcn/Makefile                                |   4 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlaudit.c                               |  62 ++++++++
 elf/Makefile                                  |   5 +
 elf/dl-audit.c                                |  46 +++---
 elf/dl-fini.c                                 |   2 +-
 elf/dl-load.c                                 |   4 +-
 elf/dl-object.c                               |   2 +-
 elf/dl-open.c                                 |   3 +-
 elf/dl-reloc.c                                |   4 +-
 elf/dl-runtime.c                              |   2 +-
 elf/dl-sym-post.h                             |   2 +-
 elf/do-rel.h                                  |   4 +-
 elf/rtld.c                                    |  44 ++++--
 elf/tst-loadaudit.c                           | 133 ++++++++++++++++++
 sysdeps/generic/ldsodefs.h                    |  11 +-
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 51 files changed, 313 insertions(+), 53 deletions(-)
 create mode 100644 dlfcn/dlaudit.c
 create mode 100644 elf/tst-loadaudit.c

-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] elf/dl-open: fix audit wrt RTLD_NOLOAD [BZ #30127]
  2023-02-21 15:33 [PATCH 0/2] minimal run-time audit support Stas Sergeev
@ 2023-02-21 15:33 ` Stas Sergeev
  2023-02-21 15:33 ` [PATCH 2/2] dlfcn,elf: impl dlload_audit_module " Stas Sergeev
  1 sibling, 0 replies; 8+ messages in thread
From: Stas Sergeev @ 2023-02-21 15:33 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

Currently dlmopen() does not allow to load objects into an audit
namespace. But the RTLD_NOLOAD case was forgotten, so the too
restrictive check prevents even getting a handle for an objects
in an audit namespace.

This patch fixes the problem by relaxing a check in case of
RTLD_NOLOAD.

Test-case on x86_64 revealed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-open.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/elf/dl-open.c b/elf/dl-open.c
index 91a2d8a538..c7ce0396d3 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -864,7 +864,8 @@ no more namespaces available for dlmopen()"));
 		  DL_NNS is 1 and so any NSID != 0 is invalid.  */
 	       || DL_NNS == 1
 	       || GL(dl_ns)[nsid]._ns_nloaded == 0
-	       || GL(dl_ns)[nsid]._ns_loaded->l_auditing))
+	       || (GL(dl_ns)[nsid]._ns_loaded->l_auditing &&
+	                           !(mode & RTLD_NOLOAD))))
     _dl_signal_error (EINVAL, file, NULL,
 		      N_("invalid target namespace in dlmopen()"));
 
-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127]
  2023-02-21 15:33 [PATCH 0/2] minimal run-time audit support Stas Sergeev
  2023-02-21 15:33 ` [PATCH 1/2] elf/dl-open: fix audit wrt RTLD_NOLOAD [BZ #30127] Stas Sergeev
@ 2023-02-21 15:33 ` Stas Sergeev
  1 sibling, 0 replies; 8+ messages in thread
From: Stas Sergeev @ 2023-02-21 15:33 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch is an impl of dlload_audit_module() function discussed
in BZ #30127, and the test-case for it, called tst-loadaudit.

dlload_audit_module (const char *file, int flags)
"file" is a module file name
"flags" are reserved for future use.

As mentioned in BZ #30134, audit module list was in RELRO so this
patch makes it writable. While I realize that some audit interfaces
(like pltenter) are the subject for hardening, I don't think I am
the right person to do any hardening on them. This patch is a bare
minimal change, and later someone can think about a hardening
restrictions, like eg disabling pltenter/pltexit audit call-backs
for synamically loaded modules.

Test-suite run on x86_64 revealed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/Makefile                                |   4 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlaudit.c                               |  62 ++++++++
 elf/Makefile                                  |   5 +
 elf/dl-audit.c                                |  46 +++---
 elf/dl-fini.c                                 |   2 +-
 elf/dl-load.c                                 |   4 +-
 elf/dl-object.c                               |   2 +-
 elf/dl-reloc.c                                |   4 +-
 elf/dl-runtime.c                              |   2 +-
 elf/dl-sym-post.h                             |   2 +-
 elf/do-rel.h                                  |   4 +-
 elf/rtld.c                                    |  44 ++++--
 elf/tst-loadaudit.c                           | 133 ++++++++++++++++++
 sysdeps/generic/ldsodefs.h                    |  11 +-
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 50 files changed, 311 insertions(+), 52 deletions(-)
 create mode 100644 dlfcn/dlaudit.c
 create mode 100644 elf/tst-loadaudit.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..68f6916d0a 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -44,8 +44,8 @@ install-lib-ldscripts = libdl.so
 $(inst_libdir)/libdl.so:
 
 ifeq ($(build-shared),yes)
-routines += dlopenold
-shared-only-routines := dlopenold
+routines += dlopenold dlaudit
+shared-only-routines := dlopenold dlaudit
 endif
 
 ifeq (yes,$(build-shared))
diff --git a/dlfcn/Versions b/dlfcn/Versions
index cc34eb824d..30145316d1 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -28,6 +28,9 @@ libc {
     dlsym;
     dlvsym;
   }
+  GLIBC_2.38 {
+    dlload_audit_module;
+  }
   GLIBC_PRIVATE {
     __libc_dlerror_result;
     _dlerror_run;
diff --git a/dlfcn/dlaudit.c b/dlfcn/dlaudit.c
new file mode 100644
index 0000000000..b124bbf932
--- /dev/null
+++ b/dlfcn/dlaudit.c
@@ -0,0 +1,62 @@
+/* Load an audit module at run time.
+   Copyright (C) 1995-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <shlib-compat.h>
+
+struct dlload_am_args
+{
+  /* The arguments for dlload_am_doit.  */
+  const char *file;
+  int flags;
+  /* The return value of dlload_am_doit.  */
+  void *new;
+};
+
+static void
+dlload_am_doit (void *a)
+{
+  struct dlload_am_args *args = (struct dlload_am_args *) a;
+
+  if (args->flags)
+    _dl_signal_error (0, NULL, NULL, _("invalid flags parameter"));
+
+  args->new = GLRO(dlload_audit_module) (args->file, args->flags);
+}
+
+static void *
+dlload_am_implementation (const char *file, int flags)
+{
+  struct dlload_am_args args;
+  args.file = file;
+  args.flags = flags;
+
+  return _dlerror_run (dlload_am_doit, &args) ? NULL : args.new;
+}
+
+void *
+___dlload_audit_module (const char *file, int flags)
+{
+  return dlload_am_implementation (file, flags);
+}
+versioned_symbol (libc, ___dlload_audit_module, dlload_audit_module,
+                  GLIBC_2_38);
diff --git a/elf/Makefile b/elf/Makefile
index 2fc6391183..433975e352 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1055,6 +1055,7 @@ ifeq (yes,$(build-shared))
 tests += \
   tst-ifunc-fault-bindnow \
   tst-ifunc-fault-lazy \
+  tst-loadaudit \
   # tests
 # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
 tests-internal += \
@@ -2349,6 +2350,10 @@ $(objpfx)tst-audit28.out: $(objpfx)tst-auditmod28.so
 $(objpfx)tst-auditmod28.so: $(libsupport)
 tst-audit28-ENV = LD_AUDIT=$(objpfx)tst-auditmod28.so
 
+$(objpfx)tst-loadaudit.out: $(objpfx)tst-auditmod18.so \
+			  $(objpfx)tst-audit18mod.so
+tst-loadaudit-ARGS = -- $(host-test-program-cmd)
+
 # tst-sonamemove links against an older implementation of the library.
 LDFLAGS-tst-sonamemove-linkmod1.so = \
   -Wl,--version-script=tst-sonamemove-linkmod1.map \
diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index 00e794aa26..579bb296f5 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -27,8 +27,8 @@
 void
 _dl_audit_activity_map (struct link_map *l, int action)
 {
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       if (afct->activity != NULL)
 	afct->activity (&link_map_audit_state (l, cnt)->cookie, action);
@@ -43,7 +43,7 @@ _dl_audit_activity_nsid (Lmid_t nsid, int action)
      does not give us a way to signal LA_ACT_CONSISTENT for it because the
      first loaded module is used to identify the namespace.  */
   struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
+  if (__glibc_likely (GL(dl_naudit) == 0)
       || head == NULL || head->l_auditing)
     return;
 
@@ -56,8 +56,8 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
   if (l == NULL || l->l_auditing || code == 0)
     return name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       if (afct->objsearch != NULL)
 	{
@@ -75,11 +75,11 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
 void
 _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (GL(dl_naudit) == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       if (afct->objopen != NULL)
 	{
@@ -95,12 +95,12 @@ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 void
 _dl_audit_objclose (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
+  if (__glibc_likely (GL(dl_naudit) == 0)
       || GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       if (afct->objclose != NULL)
 	{
@@ -116,11 +116,11 @@ _dl_audit_objclose (struct link_map *l)
 void
 _dl_audit_preinit (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (GL(dl_naudit) == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       if (afct->preinit != NULL)
 	afct->preinit (&link_map_audit_state (l, cnt)->cookie);
@@ -145,8 +145,8 @@ _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref, void **value,
   ElfW(Sym) sym = *ref;
   sym.st_value = (ElfW(Addr)) *value;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       struct auditstate *match_audit = link_map_audit_state (l, cnt);
       struct auditstate *result_audit = link_map_audit_state (result, cnt);
@@ -212,9 +212,9 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
   const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
 
   unsigned int flags = 0;
-  struct audit_ifaces *afct = GLRO(dl_audit);
+  struct audit_ifaces *afct = GL(dl_audit);
   uintptr_t new_value = (uintptr_t) sym.st_value;
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       /* XXX Check whether both DSOs must request action or only one */
       struct auditstate *l_state = link_map_audit_state (l, cnt);
@@ -267,7 +267,7 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
 		    DL_FIXUP_VALUE_TYPE *value, void *regs, long int *framesize)
 {
   /* Don't do anything if no auditor wants to intercept this call.  */
-  if (GLRO(dl_naudit) == 0
+  if (GL(dl_naudit) == 0
       || (reloc_result->enterexit & LA_SYMB_NOPLTENTER))
     return;
 
@@ -290,8 +290,8 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
   /* Keep track of overwritten addresses.  */
   unsigned int flags = reloc_result->flags;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       if (afct->ARCH_LA_PLTENTER != NULL
 	  && (reloc_result->enterexit
@@ -363,8 +363,8 @@ _dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
 					     l_info[DT_STRTAB]);
   const char *symname = strtab + sym.st_name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
     {
       if (afct->ARCH_LA_PLTEXIT != NULL
 	  && (reloc_result->enterexit
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 9acb64f47c..a96f769953 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -129,7 +129,7 @@ _dl_fini (void)
     }
 
 #ifdef SHARED
-  if (! do_audit && GLRO(dl_naudit) > 0)
+  if (! do_audit && GL(dl_naudit) > 0)
     {
       do_audit = 1;
       goto again;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fcb39a78d4..7f16b84acf 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1607,7 +1607,7 @@ open_verify (const char *name, int fd,
 
 #ifdef SHARED
   /* Give the auditing libraries a chance.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *original_name = name;
       name = _dl_audit_objsearch (name, loader, whatcode);
@@ -2001,7 +2001,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 #ifdef SHARED
   /* Give the auditing libraries a chance to change the name before we
      try anything.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *before = name;
       name = _dl_audit_objsearch (name, loader, LA_SER_ORIG);
diff --git a/elf/dl-object.c b/elf/dl-object.c
index f1f2ec956c..0e686e52a0 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -77,7 +77,7 @@ _dl_new_object (char *realname, const char *libname, int type,
       naudit = DL_NNS;
     }
   else
-    naudit = GLRO (dl_naudit);
+    naudit = GL (dl_naudit);
 #endif
 
   size_t libname_len = strlen (libname) + 1;
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 1d558c1e0c..8aedbc0d96 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -222,8 +222,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
   /* If we are auditing, install the same handlers we need for profiling.  */
   if ((reloc_mode & __RTLD_AUDIT) == 0)
     {
-      struct audit_ifaces *afct = GLRO(dl_audit);
-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+      struct audit_ifaces *afct = GL(dl_audit);
+      for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
 	{
 	  /* Profiling is needed only if PLT hooks are provided.  */
 	  if (afct->ARCH_LA_PLTENTER != NULL
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index d35a725415..672d6fb702 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -313,7 +313,7 @@ _dl_profile_fixup (
       /* Auditing checkpoint: we have a new binding.  Provide the
 	 auditing libraries the possibility to change the value and
 	 tell us whether further auditing is wanted.  */
-      if (defsym != NULL && GLRO(dl_naudit) > 0)
+      if (defsym != NULL && GL(dl_naudit) > 0)
 	_dl_audit_symbind (l, reloc_result, defsym, &value, result);
 #endif
 
diff --git a/elf/dl-sym-post.h b/elf/dl-sym-post.h
index 5623d63ac8..0cc50349ca 100644
--- a/elf/dl-sym-post.h
+++ b/elf/dl-sym-post.h
@@ -50,7 +50,7 @@ _dl_sym_post (lookup_t result, const ElfW(Sym) *ref, void *value,
   /* Auditing checkpoint: we have a new binding.  Provide the
      auditing libraries the possibility to change the value and
      tell us whether further auditing is wanted.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       if (match == NULL)
         match = _dl_sym_find_caller_link_map (caller);
diff --git a/elf/do-rel.h b/elf/do-rel.h
index 7e1cc4452a..22b5d566eb 100644
--- a/elf/do-rel.h
+++ b/elf/do-rel.h
@@ -148,7 +148,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 #if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym, rversion,
@@ -193,7 +193,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 # if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym,
diff --git a/elf/rtld.c b/elf/rtld.c
index f82fbeb132..492b81b7f5 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -151,6 +151,10 @@ static const char *audit_list_next (struct audit_list *);
 /* Initialize *STATE with the defaults.  */
 static void dl_main_state_init (struct dl_main_state *state);
 
+/* Loads audit module. */
+static void *
+_dlload_audit_module (const char *name, int flags);
+
 /* Process all environments variables the dynamic linker must recognize.
    Since all of them start with `LD_' we are a bit smarter while finding
    all the entries.  */
@@ -272,7 +276,7 @@ audit_list_next (struct audit_list *list)
     }
 }
 
-/* Count audit modules before they are loaded so GLRO(dl_naudit)
+/* Count audit modules before they are loaded so GL(dl_naudit)
    is not yet usable.  */
 static size_t
 audit_list_count (struct audit_list *list)
@@ -375,6 +379,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_error_free = _dl_error_free,
     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
     ._dl_libc_freeres = __rtld_libc_freeres,
+    ._dlload_audit_module = _dlload_audit_module,
   };
 /* If we would use strong_alias here the compiler would see a
    non-hidden definition.  This would undo the effect of the previous
@@ -930,8 +935,9 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 }
 
 /* Load one audit module.  */
-static void
-load_audit_module (const char *name, struct audit_ifaces **last_audit)
+static void *
+load_audit_module (const char *name, const void *caller_dlopen,
+                   struct audit_ifaces **last_audit)
 {
   int original_tls_idx = GL(dl_tls_max_dtv_idx);
 
@@ -946,7 +952,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
   if (__glibc_unlikely (err_str != NULL))
     {
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   struct lookup_args largs;
@@ -957,7 +963,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
     {
       unload_audit_module (dlmargs.map, original_tls_idx);
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   unsigned int (*laversion) (unsigned int) = largs.result;
@@ -977,7 +983,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
 file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 			  dlmargs.map->l_name, dlmargs.map->l_ns);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   if (!_dl_audit_check_version (lav))
@@ -986,7 +992,7 @@ file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
 			name, lav, LAV_CURRENT);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   enum { naudit_ifaces = 8 };
@@ -1032,19 +1038,31 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
   /* Now append the new auditing interface to the list.  */
   newp->ifaces.next = NULL;
   if (*last_audit == NULL)
-    *last_audit = GLRO(dl_audit) = &newp->ifaces;
+    *last_audit = GL(dl_audit) = &newp->ifaces;
   else
     *last_audit = (*last_audit)->next = &newp->ifaces;
 
   /* The dynamic linker link map is statically allocated, so the
      cookie in _dl_new_object has not happened.  */
-  link_map_audit_state (&GL (dl_rtld_map), GLRO (dl_naudit))->cookie
+  link_map_audit_state (&GL (dl_rtld_map), GL (dl_naudit))->cookie
     = (intptr_t) &GL (dl_rtld_map);
 
-  ++GLRO(dl_naudit);
+  ++GL(dl_naudit);
 
   /* Mark the DSO as being used for auditing.  */
   dlmargs.map->l_auditing = 1;
+  return dlmargs.map;
+}
+
+static void *
+_dlload_audit_module (const char *name, int flags)
+{
+  struct audit_ifaces *last_audit = GL(dl_audit);
+
+  /* Find last audit list entry. */
+  while (last_audit && last_audit->next)
+    last_audit = last_audit->next;
+  return load_audit_module (name, _dlload_audit_module, &last_audit);
 }
 
 /* Load all audit modules.  */
@@ -1058,12 +1076,12 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
       const char *name = audit_list_next (audit_list);
       if (name == NULL)
 	break;
-      load_audit_module (name, &last_audit);
+      load_audit_module (name, dl_main, &last_audit);
     }
 
   /* Notify audit modules of the initially loaded modules (the main
      program and the dynamic linker itself).  */
-  if (GLRO(dl_naudit) > 0)
+  if (GL(dl_naudit) > 0)
     {
       _dl_audit_objopen (main_map, LM_ID_BASE);
       _dl_audit_objopen (&GL(dl_rtld_map), LM_ID_BASE);
@@ -1822,7 +1840,7 @@ dl_main (const ElfW(Phdr) *phdr,
 
       /* The count based on audit strings may overestimate the number
 	 of audit modules that got loaded, but not underestimate.  */
-      assert (GLRO(dl_naudit) <= naudit);
+      assert (GL(dl_naudit) <= naudit);
     }
 
   /* Keep track of the currently loaded modules to count how many
diff --git a/elf/tst-loadaudit.c b/elf/tst-loadaudit.c
new file mode 100644
index 0000000000..914579b082
--- /dev/null
+++ b/elf/tst-loadaudit.c
@@ -0,0 +1,133 @@
+/* Check dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gnu/lib-names.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/support.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+void *
+dlload_audit_module (const char *file, int flags);
+
+static int
+handle_restart (void)
+{
+  void *ah = dlload_audit_module ("tst-auditmod18.so", 0);
+
+  TEST_VERIFY (ah != NULL);
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
+
+    pid_t (*s) (void) = xdlsym (h, "getpid");
+    TEST_COMPARE (s (), getpid ());
+
+    xdlclose (h);
+  }
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, "tst-audit18mod.so", RTLD_NOW);
+
+    int (*foo) (void) = xdlsym (h, "foo");
+    TEST_COMPARE (foo (), 10);
+
+    xdlclose (h);
+  }
+
+  return 0;
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One our fource parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name  */
+
+  if (restart)
+    return handle_restart ();
+
+  char *spargv[9];
+  int i = 0;
+  for (; i < argc - 1; i++)
+    spargv[i] = argv[i + 1];
+  spargv[i++] = (char *) "--direct";
+  spargv[i++] = (char *) "--restart";
+  spargv[i] = NULL;
+
+  struct support_capture_subprocess result
+    = support_capture_subprogram (spargv[0], spargv);
+  support_capture_subprocess_check (&result, "tst-loadaudit", 0, sc_allow_stderr);
+
+  struct
+  {
+    const char *name;
+    bool found;
+  } audit_iface[] =
+  {
+    { "la_version", false },
+    { "la_objsearch", false },
+    { "la_activity", false },
+    { "la_objopen", false },
+    { "la_objclose", false },
+#if __WORDSIZE == 32
+    { "la_symbind32", false },
+#elif __WORDSIZE == 64
+    { "la_symbind64", false },
+#endif
+  };
+
+  /* Some hooks are called more than once but the test only check if any
+     is called at least once.  */
+  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
+  TEST_VERIFY (out != NULL);
+  char *buffer = NULL;
+  size_t buffer_length = 0;
+  while (xgetline (&buffer, &buffer_length, out))
+    {
+      for (int i = 0; i < array_length (audit_iface); i++)
+	if (strncmp (buffer, audit_iface[i].name,
+		     strlen (audit_iface[i].name)) == 0)
+	  audit_iface[i].found = true;
+    }
+  free (buffer);
+  xfclose (out);
+
+  for (int i = 0; i < array_length (audit_iface); i++)
+    TEST_COMPARE (audit_iface[i].found, true);
+
+  support_capture_subprocess_free (&result);
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c99dad77cc..1a377061f8 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -399,6 +399,10 @@ struct rtld_global
   /* Used to store the audit information for the link map of the
      dynamic loader.  */
   struct auditstate _dl_rtld_auditstate[DL_NNS];
+
+  /* List of auditing interfaces.  */
+  struct audit_ifaces *_dl_audit;
+  unsigned int _dl_naudit;
 #endif
 
 #if !PTHREAD_IN_LIBC && defined SHARED \
@@ -689,12 +693,11 @@ struct rtld_global_ro
      dlopen.  */
   int (*_dl_find_object) (void *, struct dl_find_object *);
 
+  /* Loads audit module.  */
+  void *(*_dlload_audit_module) (const char *name, int flags);
+
   /* 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;
 };
 # define __rtld_global_attribute__
 # if IS_IN (rtld)
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 4e3200ef55..b3bff7135b 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2294,6 +2294,7 @@ GLIBC_2.36 arc4random_buf F
 GLIBC_2.36 arc4random_uniform F
 GLIBC_2.36 c8rtomb F
 GLIBC_2.36 mbrtoc8 F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index b66fadef40..e0defa9e33 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2633,3 +2633,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index f918bb2d48..fd72907a44 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2730,6 +2730,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 093043a533..b1cd1087bc 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2394,3 +2394,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index f28402fe03..0d124f6f73 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -514,6 +514,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index e2f56880ed..b095d01781 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -511,6 +511,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 319d92356e..25eede829b 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2670,3 +2670,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 6450e17ebe..ea3d616424 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2619,6 +2619,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 0a24ec9afd..a1b6f99e0a 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2803,6 +2803,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 02c65b6482..b8819f22c1 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2568,6 +2568,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index 62faaf4c00..bff3a565ed 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2154,3 +2154,4 @@ GLIBC_2.36 wprintf F
 GLIBC_2.36 write F
 GLIBC_2.36 writev F
 GLIBC_2.36 wscanf F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 16243a7a92..32e766d86e 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -515,6 +515,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 564a553b27..322604f951 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2746,6 +2746,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index e850f47b21..5d2745db86 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2719,3 +2719,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 37178c503f..9607647f7c 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2716,3 +2716,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 3b30b31466..010e0fdb57 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2711,6 +2711,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 0e358570a2..cf178341e9 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2709,6 +2709,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 59c598b98f..2740c1b776 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2717,6 +2717,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 2f7f1ccaf7..92cf1ff6ba 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2619,6 +2619,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 463e01ab84..b712d54d47 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2758,3 +2758,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index ffdb8819d5..38cea3234a 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2140,3 +2140,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 405d40d11c..806a3585ef 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2773,6 +2773,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index ce89602b93..ea51bbafca 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2806,6 +2806,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 849863e639..abbb22d958 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2527,6 +2527,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index b2ccee08c6..3f39ec1ea3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2829,3 +2829,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index ff90d1bff2..42fd36421a 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2396,3 +2396,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index f1017f6ec5..82e85c44c2 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2596,3 +2596,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 5ca051a9eb..4ea4a4202b 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2771,6 +2771,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 0e0b3df973..07bd8e0c36 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2564,6 +2564,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 5b48168ec6..3bb628ccab 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2626,6 +2626,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index c42b39cea8..51efda8b4f 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2623,6 +2623,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 5a0a662dee..e6e6a4f335 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2766,6 +2766,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 9ec4a0bc7f..f8bdf8b036 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2591,6 +2591,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 367c8d0a03..2dda5a2d7d 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2542,6 +2542,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 6a614efb62..23c1f31348 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2648,3 +2648,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127]
  2023-03-01 15:08 [PATCH v6 0/2] minimal run-time audit support Stas Sergeev
@ 2023-03-01 15:08 ` Stas Sergeev
  0 siblings, 0 replies; 8+ messages in thread
From: Stas Sergeev @ 2023-03-01 15:08 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch is an impl of dlload_audit_module() function discussed
in BZ #30127, and the test-case for it, called tst-loadaudit.
It checks the loading of audit module at run-time and makes sure
no "unrecognized" cookie is passed to the module call-backs.

dlload_audit_module (const char *file, int flags)
"file" is a module file name
"flags" are reserved for future use.

As mentioned in BZ #30134, audit module list was in RELRO so this
patch makes it writable. To not compromise the hardening, several
call-backs are disallowed for dynamically loaded modules. Namely
symbind, pltenter and pltexit. They have to be disallowed also because
the dynamically loaded audit module cannot interract with any object
loaded before it, which is possible in case of these 3 call-backs.
dlload_audit_module() returns a EINVAL error if such call-backs
exist in a module being loaded.

Test-suite run on x86_64 revealed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/Makefile                                |   4 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlaudit.c                               |  62 ++++++++
 elf/Makefile                                  |   6 +
 elf/dl-audit.c                                |  47 +++---
 elf/dl-fini.c                                 |   2 +-
 elf/dl-load.c                                 |   7 +-
 elf/dl-object.c                               |   4 +-
 elf/dl-reloc.c                                |   4 +-
 elf/dl-runtime.c                              |   2 +-
 elf/dl-sym-post.h                             |   2 +-
 elf/do-rel.h                                  |   4 +-
 elf/rtld.c                                    | 107 +++++++++----
 elf/tst-dynauditmod.c                         |  79 ++++++++++
 elf/tst-loadaudit.c                           | 142 ++++++++++++++++++
 include/link.h                                |   1 +
 manual/dynlink.texi                           |   1 +
 sysdeps/generic/ldsodefs.h                    |  11 +-
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 53 files changed, 450 insertions(+), 73 deletions(-)
 create mode 100644 dlfcn/dlaudit.c
 create mode 100644 elf/tst-dynauditmod.c
 create mode 100644 elf/tst-loadaudit.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..68f6916d0a 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -44,8 +44,8 @@ install-lib-ldscripts = libdl.so
 $(inst_libdir)/libdl.so:
 
 ifeq ($(build-shared),yes)
-routines += dlopenold
-shared-only-routines := dlopenold
+routines += dlopenold dlaudit
+shared-only-routines := dlopenold dlaudit
 endif
 
 ifeq (yes,$(build-shared))
diff --git a/dlfcn/Versions b/dlfcn/Versions
index cc34eb824d..30145316d1 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -28,6 +28,9 @@ libc {
     dlsym;
     dlvsym;
   }
+  GLIBC_2.38 {
+    dlload_audit_module;
+  }
   GLIBC_PRIVATE {
     __libc_dlerror_result;
     _dlerror_run;
diff --git a/dlfcn/dlaudit.c b/dlfcn/dlaudit.c
new file mode 100644
index 0000000000..b124bbf932
--- /dev/null
+++ b/dlfcn/dlaudit.c
@@ -0,0 +1,62 @@
+/* Load an audit module at run time.
+   Copyright (C) 1995-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <shlib-compat.h>
+
+struct dlload_am_args
+{
+  /* The arguments for dlload_am_doit.  */
+  const char *file;
+  int flags;
+  /* The return value of dlload_am_doit.  */
+  void *new;
+};
+
+static void
+dlload_am_doit (void *a)
+{
+  struct dlload_am_args *args = (struct dlload_am_args *) a;
+
+  if (args->flags)
+    _dl_signal_error (0, NULL, NULL, _("invalid flags parameter"));
+
+  args->new = GLRO(dlload_audit_module) (args->file, args->flags);
+}
+
+static void *
+dlload_am_implementation (const char *file, int flags)
+{
+  struct dlload_am_args args;
+  args.file = file;
+  args.flags = flags;
+
+  return _dlerror_run (dlload_am_doit, &args) ? NULL : args.new;
+}
+
+void *
+___dlload_audit_module (const char *file, int flags)
+{
+  return dlload_am_implementation (file, flags);
+}
+versioned_symbol (libc, ___dlload_audit_module, dlload_audit_module,
+                  GLIBC_2_38);
diff --git a/elf/Makefile b/elf/Makefile
index 0d19964d42..863c459bdc 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -822,6 +822,7 @@ modules-names += \
   tst-deep1mod1 \
   tst-deep1mod2 \
   tst-deep1mod3 \
+  tst-dynauditmod \
   tst-dl_find_object-mod1 \
   tst-dl_find_object-mod2 \
   tst-dl_find_object-mod3 \
@@ -1058,6 +1059,7 @@ ifeq (yes,$(build-shared))
 tests += \
   tst-ifunc-fault-bindnow \
   tst-ifunc-fault-lazy \
+  tst-loadaudit \
   # tests
 # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
 tests-internal += \
@@ -2352,6 +2354,10 @@ $(objpfx)tst-audit28.out: $(objpfx)tst-auditmod28.so
 $(objpfx)tst-auditmod28.so: $(libsupport)
 tst-audit28-ENV = LD_AUDIT=$(objpfx)tst-auditmod28.so
 
+$(objpfx)tst-loadaudit.out: $(objpfx)tst-dynauditmod.so \
+			  $(objpfx)tst-audit18mod.so
+tst-loadaudit-ARGS = -- $(host-test-program-cmd)
+
 # tst-sonamemove links against an older implementation of the library.
 LDFLAGS-tst-sonamemove-linkmod1.so = \
   -Wl,--version-script=tst-sonamemove-linkmod1.map \
diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index 00e794aa26..0770bd52b2 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -27,8 +27,8 @@
 void
 _dl_audit_activity_map (struct link_map *l, int action)
 {
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->activity != NULL)
 	afct->activity (&link_map_audit_state (l, cnt)->cookie, action);
@@ -43,8 +43,7 @@ _dl_audit_activity_nsid (Lmid_t nsid, int action)
      does not give us a way to signal LA_ACT_CONSISTENT for it because the
      first loaded module is used to identify the namespace.  */
   struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
-      || head == NULL || head->l_auditing)
+  if (__glibc_likely (head == NULL || head->l_naudit == 0 || head->l_auditing))
     return;
 
   _dl_audit_activity_map (head, action);
@@ -56,8 +55,8 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
   if (l == NULL || l->l_auditing || code == 0)
     return name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objsearch != NULL)
 	{
@@ -75,11 +74,11 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
 void
 _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objopen != NULL)
 	{
@@ -95,12 +94,12 @@ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 void
 _dl_audit_objclose (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
+  if (__glibc_likely (l->l_naudit == 0)
       || GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objclose != NULL)
 	{
@@ -116,11 +115,11 @@ _dl_audit_objclose (struct link_map *l)
 void
 _dl_audit_preinit (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->preinit != NULL)
 	afct->preinit (&link_map_audit_state (l, cnt)->cookie);
@@ -145,8 +144,8 @@ _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref, void **value,
   ElfW(Sym) sym = *ref;
   sym.st_value = (ElfW(Addr)) *value;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       struct auditstate *match_audit = link_map_audit_state (l, cnt);
       struct auditstate *result_audit = link_map_audit_state (result, cnt);
@@ -212,9 +211,9 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
   const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
 
   unsigned int flags = 0;
-  struct audit_ifaces *afct = GLRO(dl_audit);
+  struct audit_ifaces *afct = GL(dl_audit);
   uintptr_t new_value = (uintptr_t) sym.st_value;
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       /* XXX Check whether both DSOs must request action or only one */
       struct auditstate *l_state = link_map_audit_state (l, cnt);
@@ -267,7 +266,7 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
 		    DL_FIXUP_VALUE_TYPE *value, void *regs, long int *framesize)
 {
   /* Don't do anything if no auditor wants to intercept this call.  */
-  if (GLRO(dl_naudit) == 0
+  if (l->l_naudit == 0
       || (reloc_result->enterexit & LA_SYMB_NOPLTENTER))
     return;
 
@@ -290,8 +289,8 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
   /* Keep track of overwritten addresses.  */
   unsigned int flags = reloc_result->flags;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTENTER != NULL
 	  && (reloc_result->enterexit
@@ -363,8 +362,8 @@ _dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
 					     l_info[DT_STRTAB]);
   const char *symname = strtab + sym.st_name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTEXIT != NULL
 	  && (reloc_result->enterexit
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 9acb64f47c..a96f769953 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -129,7 +129,7 @@ _dl_fini (void)
     }
 
 #ifdef SHARED
-  if (! do_audit && GLRO(dl_naudit) > 0)
+  if (! do_audit && GL(dl_naudit) > 0)
     {
       do_audit = 1;
       goto again;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fcb39a78d4..720324ccfb 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1032,6 +1032,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
       l->l_addr = l->l_real->l_addr;
       l->l_ld = l->l_real->l_ld;
 
+      /* Do not call audit when this lm closes. */
+      l->l_naudit = 0;
+
       /* No need to bump the refcount of the real object, ld.so will
 	 never be unloaded.  */
       __close_nocancel (fd);
@@ -1607,7 +1610,7 @@ open_verify (const char *name, int fd,
 
 #ifdef SHARED
   /* Give the auditing libraries a chance.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *original_name = name;
       name = _dl_audit_objsearch (name, loader, whatcode);
@@ -2001,7 +2004,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 #ifdef SHARED
   /* Give the auditing libraries a chance to change the name before we
      try anything.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *before = name;
       name = _dl_audit_objsearch (name, loader, LA_SER_ORIG);
diff --git a/elf/dl-object.c b/elf/dl-object.c
index f1f2ec956c..497c57154a 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -77,7 +77,7 @@ _dl_new_object (char *realname, const char *libname, int type,
       naudit = DL_NNS;
     }
   else
-    naudit = GLRO (dl_naudit);
+    naudit = GL (dl_naudit);
 #endif
 
   size_t libname_len = strlen (libname) + 1;
@@ -136,6 +136,8 @@ _dl_new_object (char *realname, const char *libname, int type,
   new->l_ns = nsid;
 
 #ifdef SHARED
+  /* GL(dl_naudit) is a current value and naudit is maximum value. */
+  new->l_naudit = GL (dl_naudit);
   for (unsigned int cnt = 0; cnt < naudit; ++cnt)
     /* No need to initialize bindflags due to calloc.  */
     link_map_audit_state (new, cnt)->cookie = (uintptr_t) new;
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 1d558c1e0c..8aedbc0d96 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -222,8 +222,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
   /* If we are auditing, install the same handlers we need for profiling.  */
   if ((reloc_mode & __RTLD_AUDIT) == 0)
     {
-      struct audit_ifaces *afct = GLRO(dl_audit);
-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+      struct audit_ifaces *afct = GL(dl_audit);
+      for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
 	{
 	  /* Profiling is needed only if PLT hooks are provided.  */
 	  if (afct->ARCH_LA_PLTENTER != NULL
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index d35a725415..672d6fb702 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -313,7 +313,7 @@ _dl_profile_fixup (
       /* Auditing checkpoint: we have a new binding.  Provide the
 	 auditing libraries the possibility to change the value and
 	 tell us whether further auditing is wanted.  */
-      if (defsym != NULL && GLRO(dl_naudit) > 0)
+      if (defsym != NULL && GL(dl_naudit) > 0)
 	_dl_audit_symbind (l, reloc_result, defsym, &value, result);
 #endif
 
diff --git a/elf/dl-sym-post.h b/elf/dl-sym-post.h
index 5623d63ac8..0cc50349ca 100644
--- a/elf/dl-sym-post.h
+++ b/elf/dl-sym-post.h
@@ -50,7 +50,7 @@ _dl_sym_post (lookup_t result, const ElfW(Sym) *ref, void *value,
   /* Auditing checkpoint: we have a new binding.  Provide the
      auditing libraries the possibility to change the value and
      tell us whether further auditing is wanted.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       if (match == NULL)
         match = _dl_sym_find_caller_link_map (caller);
diff --git a/elf/do-rel.h b/elf/do-rel.h
index 7e1cc4452a..22b5d566eb 100644
--- a/elf/do-rel.h
+++ b/elf/do-rel.h
@@ -148,7 +148,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 #if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym, rversion,
@@ -193,7 +193,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 # if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym,
diff --git a/elf/rtld.c b/elf/rtld.c
index f82fbeb132..714152f4b7 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -151,6 +151,10 @@ static const char *audit_list_next (struct audit_list *);
 /* Initialize *STATE with the defaults.  */
 static void dl_main_state_init (struct dl_main_state *state);
 
+/* Loads audit module. */
+static void *
+_dlload_audit_module (const char *name, int flags);
+
 /* Process all environments variables the dynamic linker must recognize.
    Since all of them start with `LD_' we are a bit smarter while finding
    all the entries.  */
@@ -272,7 +276,7 @@ audit_list_next (struct audit_list *list)
     }
 }
 
-/* Count audit modules before they are loaded so GLRO(dl_naudit)
+/* Count audit modules before they are loaded so GL(dl_naudit)
    is not yet usable.  */
 static size_t
 audit_list_count (struct audit_list *list)
@@ -375,6 +379,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_error_free = _dl_error_free,
     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
     ._dl_libc_freeres = __rtld_libc_freeres,
+    ._dlload_audit_module = _dlload_audit_module,
   };
 /* If we would use strong_alias here the compiler would see a
    non-hidden definition.  This would undo the effect of the previous
@@ -930,8 +935,9 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 }
 
 /* Load one audit module.  */
-static void
-load_audit_module (const char *name, struct audit_ifaces **last_audit)
+static void *
+_load_audit_module (const char *name, const void *caller_dlopen,
+                   struct audit_ifaces **last_audit, bool dynload)
 {
   int original_tls_idx = GL(dl_tls_max_dtv_idx);
 
@@ -946,7 +952,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
   if (__glibc_unlikely (err_str != NULL))
     {
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   struct lookup_args largs;
@@ -957,7 +963,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
     {
       unload_audit_module (dlmargs.map, original_tls_idx);
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   unsigned int (*laversion) (unsigned int) = largs.result;
@@ -977,7 +983,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
 file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 			  dlmargs.map->l_name, dlmargs.map->l_ns);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   if (!_dl_audit_check_version (lav))
@@ -986,7 +992,7 @@ file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
 			name, lav, LAV_CURRENT);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   enum { naudit_ifaces = 8 };
@@ -998,53 +1004,88 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
   if (newp == NULL)
     _dl_fatal_printf ("Out of memory while loading audit modules\n");
 
-  /* Names of the auditing interfaces.  All in one
-     long string.  */
-  static const char audit_iface_names[] =
-    "la_activity\0"
-    "la_objsearch\0"
-    "la_objopen\0"
-    "la_preinit\0"
-    LA_SYMBIND "\0"
+  /* Names of the auditing interfaces.  */
+  struct audit_ifname
+  {
+    const char *name;
+    bool dyn_allowed;
+  } audit_iface_names[] =
+  {
+    { "la_activity", true },
+    { "la_objsearch", true },
+    { "la_objopen", true },
+    { "la_preinit", true },
+    { LA_SYMBIND, false },
 #define STRING(s) __STRING (s)
-    "la_" STRING (ARCH_LA_PLTENTER) "\0"
-    "la_" STRING (ARCH_LA_PLTEXIT) "\0"
-    "la_objclose\0";
-  unsigned int cnt = 0;
-  const char *cp = audit_iface_names;
-  do
+    { "la_" STRING (ARCH_LA_PLTENTER), false },
+    { "la_" STRING (ARCH_LA_PLTEXIT), false },
+    { "la_objclose", true },
+  };
+  unsigned int cnt;
+  for (cnt = 0; cnt < naudit_ifaces; cnt++)
     {
-      largs.name = cp;
+      struct audit_ifname *cp = &audit_iface_names[cnt];
+      largs.name = cp->name;
       _dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
 
       /* Store the pointer.  */
       if (err_str == NULL && largs.result != NULL)
-	newp->fptr[cnt] = largs.result;
+        {
+          if (dynload && !cp->dyn_allowed)
+            {
+              unload_audit_module (dlmargs.map, original_tls_idx);
+              _dl_signal_error (EINVAL, name, NULL, N_("\
+unacceptable interfaces for dlload_audit_module()"));
+              return NULL;
+            }
+          else
+	    newp->fptr[cnt] = largs.result;
+	}
       else
 	newp->fptr[cnt] = NULL;
-      ++cnt;
-
-      cp = strchr (cp, '\0') + 1;
     }
-  while (*cp != '\0');
-  assert (cnt == naudit_ifaces);
 
   /* Now append the new auditing interface to the list.  */
   newp->ifaces.next = NULL;
   if (*last_audit == NULL)
-    *last_audit = GLRO(dl_audit) = &newp->ifaces;
+    *last_audit = GL(dl_audit) = &newp->ifaces;
   else
     *last_audit = (*last_audit)->next = &newp->ifaces;
 
   /* The dynamic linker link map is statically allocated, so the
      cookie in _dl_new_object has not happened.  */
-  link_map_audit_state (&GL (dl_rtld_map), GLRO (dl_naudit))->cookie
+  link_map_audit_state (&GL (dl_rtld_map), GL (dl_naudit))->cookie
     = (intptr_t) &GL (dl_rtld_map);
 
-  ++GLRO(dl_naudit);
+  ++GL(dl_naudit);
 
   /* Mark the DSO as being used for auditing.  */
   dlmargs.map->l_auditing = 1;
+  return dlmargs.map;
+}
+
+static void
+load_audit_module (const char *name, struct audit_ifaces **last_audit)
+{
+  if (_load_audit_module (name, dl_main, last_audit, false))
+    {
+      struct link_map *l;
+
+      /* Global audit module can audit all existing objects. */
+      for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l; l = l->l_next)
+        l->l_naudit++;
+    }
+}
+
+static void *
+_dlload_audit_module (const char *name, int flags)
+{
+  struct audit_ifaces *last_audit = GL(dl_audit);
+
+  /* Find last audit list entry. */
+  while (last_audit && last_audit->next)
+    last_audit = last_audit->next;
+  return _load_audit_module (name, _dlload_audit_module, &last_audit, true);
 }
 
 /* Load all audit modules.  */
@@ -1063,7 +1104,7 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
 
   /* Notify audit modules of the initially loaded modules (the main
      program and the dynamic linker itself).  */
-  if (GLRO(dl_naudit) > 0)
+  if (GL(dl_naudit) > 0)
     {
       _dl_audit_objopen (main_map, LM_ID_BASE);
       _dl_audit_objopen (&GL(dl_rtld_map), LM_ID_BASE);
@@ -1822,7 +1863,7 @@ dl_main (const ElfW(Phdr) *phdr,
 
       /* The count based on audit strings may overestimate the number
 	 of audit modules that got loaded, but not underestimate.  */
-      assert (GLRO(dl_naudit) <= naudit);
+      assert (GL(dl_naudit) <= naudit);
     }
 
   /* Keep track of the currently loaded modules to count how many
diff --git a/elf/tst-dynauditmod.c b/elf/tst-dynauditmod.c
new file mode 100644
index 0000000000..46941f7d37
--- /dev/null
+++ b/elf/tst-dynauditmod.c
@@ -0,0 +1,79 @@
+/* Audit mod for dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <link.h>
+#include <assert.h>
+
+/* la_objopen() sets the cookie to this value. Other call-backs check
+ * the value to see if la_objopen() was not somehow skipped. */
+#define TST_COOKIE_VAL 12
+
+unsigned int
+la_version (unsigned int version)
+{
+  fprintf (stderr, "%s\n", __func__);
+  return LAV_CURRENT;
+}
+
+char *
+la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+  return (char *) name;
+}
+
+void
+la_activity (uintptr_t *cookie, unsigned int flag)
+{
+  struct link_map *map = (void *) *cookie;
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+}
+
+unsigned int
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+}
+
+unsigned int
+la_objclose (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    {
+      struct link_map *map = (void *) *cookie;
+      fprintf (stderr, "%s\n", map->l_name);
+    }
+  assert (*cookie == TST_COOKIE_VAL);
+  return 0;
+}
+
+void
+la_preinit (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+}
diff --git a/elf/tst-loadaudit.c b/elf/tst-loadaudit.c
new file mode 100644
index 0000000000..f6db808afb
--- /dev/null
+++ b/elf/tst-loadaudit.c
@@ -0,0 +1,142 @@
+/* Check dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gnu/lib-names.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/support.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+void *
+dlload_audit_module (const char *file, int flags);
+
+static int
+handle_restart (void)
+{
+  void *ah;
+
+  ah = dlload_audit_module ("tst-auditmod18.so", 0);
+  TEST_VERIFY (ah == NULL);
+  fprintf (stderr, "%s\n", dlerror ());
+  ah = dlload_audit_module ("tst-dynauditmod.so", 0);
+  TEST_VERIFY (ah != NULL);
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
+
+    pid_t (*s) (void) = xdlsym (h, "getpid");
+    TEST_COMPARE (s (), getpid ());
+
+    xdlclose (h);
+  }
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, "tst-audit18mod.so", RTLD_NOW);
+
+    int (*foo) (void) = xdlsym (h, "foo");
+    TEST_COMPARE (foo (), 10);
+
+    xdlclose (h);
+  }
+
+  return 0;
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One our fource parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name  */
+
+  if (restart)
+    return handle_restart ();
+
+  char *spargv[9];
+  int i = 0;
+  for (; i < argc - 1; i++)
+    spargv[i] = argv[i + 1];
+  spargv[i++] = (char *) "--direct";
+  spargv[i++] = (char *) "--restart";
+  spargv[i] = NULL;
+
+  struct support_capture_subprocess result
+    = support_capture_subprogram (spargv[0], spargv);
+  support_capture_subprocess_check (&result, "tst-loadaudit", 0, sc_allow_stderr);
+
+  struct
+  {
+    const char *name;
+    bool found;
+  } audit_iface[] =
+  {
+    { "la_version", false },
+    { "la_objsearch", false },
+    { "la_activity", false },
+    { "la_objopen", false },
+    { "la_objclose", false },
+#if __WORDSIZE == 32
+    { "la_symbind32", false },
+#elif __WORDSIZE == 64
+    { "la_symbind64", false },
+#endif
+#define STRING(s) __STRING (s)
+    { "la_" STRING (ARCH_LA_PLTENTER), false },
+    { "la_" STRING (ARCH_LA_PLTEXIT), false },
+  };
+
+  /* Some hooks are called more than once but the test only check if any
+     is called at least once.  */
+  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
+  TEST_VERIFY (out != NULL);
+  char *buffer = NULL;
+  size_t buffer_length = 0;
+  while (xgetline (&buffer, &buffer_length, out))
+    {
+      for (int i = 0; i < array_length (audit_iface); i++)
+	if (strncmp (buffer, audit_iface[i].name,
+		     strlen (audit_iface[i].name)) == 0)
+	  audit_iface[i].found = true;
+    }
+  free (buffer);
+  xfclose (out);
+
+  /* Check that symbols are resolved, except for the last 3.
+     symbind, pltenter, pltexit are not allowed for dynamically loaded mod. */
+  for (int i = 0; i < array_length (audit_iface); i++)
+    TEST_COMPARE (audit_iface[i].found, i < array_length (audit_iface) - 3);
+
+  support_capture_subprocess_free (&result);
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/include/link.h b/include/link.h
index 1d74feb2bd..31f34540fc 100644
--- a/include/link.h
+++ b/include/link.h
@@ -347,6 +347,7 @@ struct link_map
     size_t l_relro_size;
 
     unsigned long long int l_serial;
+    unsigned int l_naudit;
   };
 
 #include <dl-relocate-ld.h>
diff --git a/manual/dynlink.texi b/manual/dynlink.texi
index 6a4a50d3f0..898354ebe9 100644
--- a/manual/dynlink.texi
+++ b/manual/dynlink.texi
@@ -209,6 +209,7 @@ This function is a GNU extension.
 @c dladdr1
 @c dlclose
 @c dlerror
+@c dlload_audit_module
 @c dlmopen
 @c dlopen
 @c dlsym
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c99dad77cc..1a377061f8 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -399,6 +399,10 @@ struct rtld_global
   /* Used to store the audit information for the link map of the
      dynamic loader.  */
   struct auditstate _dl_rtld_auditstate[DL_NNS];
+
+  /* List of auditing interfaces.  */
+  struct audit_ifaces *_dl_audit;
+  unsigned int _dl_naudit;
 #endif
 
 #if !PTHREAD_IN_LIBC && defined SHARED \
@@ -689,12 +693,11 @@ struct rtld_global_ro
      dlopen.  */
   int (*_dl_find_object) (void *, struct dl_find_object *);
 
+  /* Loads audit module.  */
+  void *(*_dlload_audit_module) (const char *name, int flags);
+
   /* 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;
 };
 # define __rtld_global_attribute__
 # if IS_IN (rtld)
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index a0419a13d0..c4618b690d 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2314,6 +2314,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 62e80648e8..6d29418617 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2653,3 +2653,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 9d490fdee8..14376cece0 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2750,6 +2750,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 50874e92fc..e936e68b4f 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2414,3 +2414,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 544b5b2741..ac7f0b6116 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -534,6 +534,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index da532a0191..4452bd3079 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -531,6 +531,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 6c74a60d69..68e22923fc 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2690,3 +2690,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 13d30e646f..f0a1f78c68 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2639,6 +2639,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index f5dfa2a20e..32516113f9 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2823,6 +2823,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 58f1526030..5810db8371 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2588,6 +2588,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index 46ce2437fe..72f140e522 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2174,3 +2174,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index f34085ce35..9ead06cdf0 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -535,6 +535,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 349377d154..c3aee9fa16 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2766,6 +2766,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 38e7fb9b2a..7cb4e492a4 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2739,3 +2739,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index ec4ca27b75..e1d6672d64 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2736,3 +2736,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index bd3f3404fb..3e29d05c1b 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2731,6 +2731,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 9b09fab6ec..36d550962d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2729,6 +2729,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 3b8f2b8ca3..f2773be8d6 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2737,6 +2737,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 497553414d..65d15b6f03 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2639,6 +2639,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index f67f241498..c64625787b 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2778,3 +2778,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index a59a58f44c..8f6e1c7a59 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2160,3 +2160,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index a1bcf79955..e79b46489f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2793,6 +2793,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index c0f28aea45..94dc5ba72c 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2826,6 +2826,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 6b4459964f..9a0401ef37 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2547,6 +2547,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index e90fb502d2..bbdc0d40d3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2849,3 +2849,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index 29be561b60..fad99a0af2 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2416,3 +2416,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 506a4e6a65..1f3e3a81d9 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2616,3 +2616,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 976cd741ee..5ad8f9e052 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2791,6 +2791,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 909ec927dc..3532cb3b68 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2584,6 +2584,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 31a777c4aa..93d7641f76 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2646,6 +2646,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 8d43e8c952..6134cec1e4 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2643,6 +2643,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 91c552dc4c..cda6bb9e29 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2786,6 +2786,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index c5c5e5cf9a..59435bd0b8 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2611,6 +2611,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index e51996e046..c52cbd8aba 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2562,6 +2562,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 388536b3be..f830b889ca 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2668,3 +2668,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127]
  2023-02-28 16:51 [PATCH v5 0/2] minimal run-time audit support Stas Sergeev
@ 2023-02-28 16:51 ` Stas Sergeev
  0 siblings, 0 replies; 8+ messages in thread
From: Stas Sergeev @ 2023-02-28 16:51 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch is an impl of dlload_audit_module() function discussed
in BZ #30127, and the test-case for it, called tst-loadaudit.
It checks the loading of audit module at run-time and makes sure
no "unrecognized" cookie is passed to the module call-backs.

dlload_audit_module (const char *file, int flags)
"file" is a module file name
"flags" are reserved for future use.

As mentioned in BZ #30134, audit module list was in RELRO so this
patch makes it writable. To not compromise the hardening, several
call-backs are disallowed for dynamically loaded modules. Namely
symbind, pltenter and pltexit. They have to be disallowed also because
the dynamically loaded audit module cannot interract with any object
loaded before it, which is possible in case of these 3 call-backs.
dlload_audit_module() returns a EINVAL error if such call-backs
exist in a module being loaded.

Test-suite run on x86_64 revealed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/Makefile                                |   4 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlaudit.c                               |  62 ++++++++
 elf/Makefile                                  |   6 +
 elf/dl-audit.c                                |  47 +++---
 elf/dl-fini.c                                 |   2 +-
 elf/dl-load.c                                 |   7 +-
 elf/dl-object.c                               |   4 +-
 elf/dl-reloc.c                                |   4 +-
 elf/dl-runtime.c                              |   2 +-
 elf/dl-sym-post.h                             |   2 +-
 elf/do-rel.h                                  |   4 +-
 elf/rtld.c                                    | 107 +++++++++----
 elf/tst-dynauditmod.c                         |  79 ++++++++++
 elf/tst-loadaudit.c                           | 142 ++++++++++++++++++
 include/link.h                                |   1 +
 manual/dynlink.texi                           |   1 +
 sysdeps/generic/ldsodefs.h                    |  11 +-
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 53 files changed, 450 insertions(+), 73 deletions(-)
 create mode 100644 dlfcn/dlaudit.c
 create mode 100644 elf/tst-dynauditmod.c
 create mode 100644 elf/tst-loadaudit.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..68f6916d0a 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -44,8 +44,8 @@ install-lib-ldscripts = libdl.so
 $(inst_libdir)/libdl.so:
 
 ifeq ($(build-shared),yes)
-routines += dlopenold
-shared-only-routines := dlopenold
+routines += dlopenold dlaudit
+shared-only-routines := dlopenold dlaudit
 endif
 
 ifeq (yes,$(build-shared))
diff --git a/dlfcn/Versions b/dlfcn/Versions
index cc34eb824d..30145316d1 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -28,6 +28,9 @@ libc {
     dlsym;
     dlvsym;
   }
+  GLIBC_2.38 {
+    dlload_audit_module;
+  }
   GLIBC_PRIVATE {
     __libc_dlerror_result;
     _dlerror_run;
diff --git a/dlfcn/dlaudit.c b/dlfcn/dlaudit.c
new file mode 100644
index 0000000000..b124bbf932
--- /dev/null
+++ b/dlfcn/dlaudit.c
@@ -0,0 +1,62 @@
+/* Load an audit module at run time.
+   Copyright (C) 1995-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <shlib-compat.h>
+
+struct dlload_am_args
+{
+  /* The arguments for dlload_am_doit.  */
+  const char *file;
+  int flags;
+  /* The return value of dlload_am_doit.  */
+  void *new;
+};
+
+static void
+dlload_am_doit (void *a)
+{
+  struct dlload_am_args *args = (struct dlload_am_args *) a;
+
+  if (args->flags)
+    _dl_signal_error (0, NULL, NULL, _("invalid flags parameter"));
+
+  args->new = GLRO(dlload_audit_module) (args->file, args->flags);
+}
+
+static void *
+dlload_am_implementation (const char *file, int flags)
+{
+  struct dlload_am_args args;
+  args.file = file;
+  args.flags = flags;
+
+  return _dlerror_run (dlload_am_doit, &args) ? NULL : args.new;
+}
+
+void *
+___dlload_audit_module (const char *file, int flags)
+{
+  return dlload_am_implementation (file, flags);
+}
+versioned_symbol (libc, ___dlload_audit_module, dlload_audit_module,
+                  GLIBC_2_38);
diff --git a/elf/Makefile b/elf/Makefile
index 0d19964d42..863c459bdc 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -822,6 +822,7 @@ modules-names += \
   tst-deep1mod1 \
   tst-deep1mod2 \
   tst-deep1mod3 \
+  tst-dynauditmod \
   tst-dl_find_object-mod1 \
   tst-dl_find_object-mod2 \
   tst-dl_find_object-mod3 \
@@ -1058,6 +1059,7 @@ ifeq (yes,$(build-shared))
 tests += \
   tst-ifunc-fault-bindnow \
   tst-ifunc-fault-lazy \
+  tst-loadaudit \
   # tests
 # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
 tests-internal += \
@@ -2352,6 +2354,10 @@ $(objpfx)tst-audit28.out: $(objpfx)tst-auditmod28.so
 $(objpfx)tst-auditmod28.so: $(libsupport)
 tst-audit28-ENV = LD_AUDIT=$(objpfx)tst-auditmod28.so
 
+$(objpfx)tst-loadaudit.out: $(objpfx)tst-dynauditmod.so \
+			  $(objpfx)tst-audit18mod.so
+tst-loadaudit-ARGS = -- $(host-test-program-cmd)
+
 # tst-sonamemove links against an older implementation of the library.
 LDFLAGS-tst-sonamemove-linkmod1.so = \
   -Wl,--version-script=tst-sonamemove-linkmod1.map \
diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index 00e794aa26..0770bd52b2 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -27,8 +27,8 @@
 void
 _dl_audit_activity_map (struct link_map *l, int action)
 {
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->activity != NULL)
 	afct->activity (&link_map_audit_state (l, cnt)->cookie, action);
@@ -43,8 +43,7 @@ _dl_audit_activity_nsid (Lmid_t nsid, int action)
      does not give us a way to signal LA_ACT_CONSISTENT for it because the
      first loaded module is used to identify the namespace.  */
   struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
-      || head == NULL || head->l_auditing)
+  if (__glibc_likely (head == NULL || head->l_naudit == 0 || head->l_auditing))
     return;
 
   _dl_audit_activity_map (head, action);
@@ -56,8 +55,8 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
   if (l == NULL || l->l_auditing || code == 0)
     return name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objsearch != NULL)
 	{
@@ -75,11 +74,11 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
 void
 _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objopen != NULL)
 	{
@@ -95,12 +94,12 @@ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 void
 _dl_audit_objclose (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
+  if (__glibc_likely (l->l_naudit == 0)
       || GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objclose != NULL)
 	{
@@ -116,11 +115,11 @@ _dl_audit_objclose (struct link_map *l)
 void
 _dl_audit_preinit (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->preinit != NULL)
 	afct->preinit (&link_map_audit_state (l, cnt)->cookie);
@@ -145,8 +144,8 @@ _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref, void **value,
   ElfW(Sym) sym = *ref;
   sym.st_value = (ElfW(Addr)) *value;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       struct auditstate *match_audit = link_map_audit_state (l, cnt);
       struct auditstate *result_audit = link_map_audit_state (result, cnt);
@@ -212,9 +211,9 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
   const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
 
   unsigned int flags = 0;
-  struct audit_ifaces *afct = GLRO(dl_audit);
+  struct audit_ifaces *afct = GL(dl_audit);
   uintptr_t new_value = (uintptr_t) sym.st_value;
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       /* XXX Check whether both DSOs must request action or only one */
       struct auditstate *l_state = link_map_audit_state (l, cnt);
@@ -267,7 +266,7 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
 		    DL_FIXUP_VALUE_TYPE *value, void *regs, long int *framesize)
 {
   /* Don't do anything if no auditor wants to intercept this call.  */
-  if (GLRO(dl_naudit) == 0
+  if (l->l_naudit == 0
       || (reloc_result->enterexit & LA_SYMB_NOPLTENTER))
     return;
 
@@ -290,8 +289,8 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
   /* Keep track of overwritten addresses.  */
   unsigned int flags = reloc_result->flags;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTENTER != NULL
 	  && (reloc_result->enterexit
@@ -363,8 +362,8 @@ _dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
 					     l_info[DT_STRTAB]);
   const char *symname = strtab + sym.st_name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTEXIT != NULL
 	  && (reloc_result->enterexit
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 9acb64f47c..a96f769953 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -129,7 +129,7 @@ _dl_fini (void)
     }
 
 #ifdef SHARED
-  if (! do_audit && GLRO(dl_naudit) > 0)
+  if (! do_audit && GL(dl_naudit) > 0)
     {
       do_audit = 1;
       goto again;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fcb39a78d4..720324ccfb 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1032,6 +1032,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
       l->l_addr = l->l_real->l_addr;
       l->l_ld = l->l_real->l_ld;
 
+      /* Do not call audit when this lm closes. */
+      l->l_naudit = 0;
+
       /* No need to bump the refcount of the real object, ld.so will
 	 never be unloaded.  */
       __close_nocancel (fd);
@@ -1607,7 +1610,7 @@ open_verify (const char *name, int fd,
 
 #ifdef SHARED
   /* Give the auditing libraries a chance.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *original_name = name;
       name = _dl_audit_objsearch (name, loader, whatcode);
@@ -2001,7 +2004,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 #ifdef SHARED
   /* Give the auditing libraries a chance to change the name before we
      try anything.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *before = name;
       name = _dl_audit_objsearch (name, loader, LA_SER_ORIG);
diff --git a/elf/dl-object.c b/elf/dl-object.c
index f1f2ec956c..497c57154a 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -77,7 +77,7 @@ _dl_new_object (char *realname, const char *libname, int type,
       naudit = DL_NNS;
     }
   else
-    naudit = GLRO (dl_naudit);
+    naudit = GL (dl_naudit);
 #endif
 
   size_t libname_len = strlen (libname) + 1;
@@ -136,6 +136,8 @@ _dl_new_object (char *realname, const char *libname, int type,
   new->l_ns = nsid;
 
 #ifdef SHARED
+  /* GL(dl_naudit) is a current value and naudit is maximum value. */
+  new->l_naudit = GL (dl_naudit);
   for (unsigned int cnt = 0; cnt < naudit; ++cnt)
     /* No need to initialize bindflags due to calloc.  */
     link_map_audit_state (new, cnt)->cookie = (uintptr_t) new;
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 1d558c1e0c..8aedbc0d96 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -222,8 +222,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
   /* If we are auditing, install the same handlers we need for profiling.  */
   if ((reloc_mode & __RTLD_AUDIT) == 0)
     {
-      struct audit_ifaces *afct = GLRO(dl_audit);
-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+      struct audit_ifaces *afct = GL(dl_audit);
+      for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
 	{
 	  /* Profiling is needed only if PLT hooks are provided.  */
 	  if (afct->ARCH_LA_PLTENTER != NULL
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index d35a725415..672d6fb702 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -313,7 +313,7 @@ _dl_profile_fixup (
       /* Auditing checkpoint: we have a new binding.  Provide the
 	 auditing libraries the possibility to change the value and
 	 tell us whether further auditing is wanted.  */
-      if (defsym != NULL && GLRO(dl_naudit) > 0)
+      if (defsym != NULL && GL(dl_naudit) > 0)
 	_dl_audit_symbind (l, reloc_result, defsym, &value, result);
 #endif
 
diff --git a/elf/dl-sym-post.h b/elf/dl-sym-post.h
index 5623d63ac8..0cc50349ca 100644
--- a/elf/dl-sym-post.h
+++ b/elf/dl-sym-post.h
@@ -50,7 +50,7 @@ _dl_sym_post (lookup_t result, const ElfW(Sym) *ref, void *value,
   /* Auditing checkpoint: we have a new binding.  Provide the
      auditing libraries the possibility to change the value and
      tell us whether further auditing is wanted.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       if (match == NULL)
         match = _dl_sym_find_caller_link_map (caller);
diff --git a/elf/do-rel.h b/elf/do-rel.h
index 7e1cc4452a..22b5d566eb 100644
--- a/elf/do-rel.h
+++ b/elf/do-rel.h
@@ -148,7 +148,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 #if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym, rversion,
@@ -193,7 +193,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 # if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym,
diff --git a/elf/rtld.c b/elf/rtld.c
index f82fbeb132..3c1cc030c1 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -151,6 +151,10 @@ static const char *audit_list_next (struct audit_list *);
 /* Initialize *STATE with the defaults.  */
 static void dl_main_state_init (struct dl_main_state *state);
 
+/* Loads audit module. */
+static void *
+_dlload_audit_module (const char *name, int flags);
+
 /* Process all environments variables the dynamic linker must recognize.
    Since all of them start with `LD_' we are a bit smarter while finding
    all the entries.  */
@@ -272,7 +276,7 @@ audit_list_next (struct audit_list *list)
     }
 }
 
-/* Count audit modules before they are loaded so GLRO(dl_naudit)
+/* Count audit modules before they are loaded so GL(dl_naudit)
    is not yet usable.  */
 static size_t
 audit_list_count (struct audit_list *list)
@@ -375,6 +379,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_error_free = _dl_error_free,
     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
     ._dl_libc_freeres = __rtld_libc_freeres,
+    ._dlload_audit_module = _dlload_audit_module,
   };
 /* If we would use strong_alias here the compiler would see a
    non-hidden definition.  This would undo the effect of the previous
@@ -930,8 +935,9 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 }
 
 /* Load one audit module.  */
-static void
-load_audit_module (const char *name, struct audit_ifaces **last_audit)
+static void *
+_load_audit_module (const char *name, const void *caller_dlopen,
+                   struct audit_ifaces **last_audit, bool dynload)
 {
   int original_tls_idx = GL(dl_tls_max_dtv_idx);
 
@@ -946,7 +952,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
   if (__glibc_unlikely (err_str != NULL))
     {
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   struct lookup_args largs;
@@ -957,7 +963,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
     {
       unload_audit_module (dlmargs.map, original_tls_idx);
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   unsigned int (*laversion) (unsigned int) = largs.result;
@@ -977,7 +983,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
 file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 			  dlmargs.map->l_name, dlmargs.map->l_ns);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   if (!_dl_audit_check_version (lav))
@@ -986,7 +992,7 @@ file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
 			name, lav, LAV_CURRENT);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   enum { naudit_ifaces = 8 };
@@ -998,53 +1004,88 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
   if (newp == NULL)
     _dl_fatal_printf ("Out of memory while loading audit modules\n");
 
-  /* Names of the auditing interfaces.  All in one
-     long string.  */
-  static const char audit_iface_names[] =
-    "la_activity\0"
-    "la_objsearch\0"
-    "la_objopen\0"
-    "la_preinit\0"
-    LA_SYMBIND "\0"
+  /* Names of the auditing interfaces.  */
+  struct audit_ifname
+  {
+    const char *name;
+    bool dyn_allowed;
+  } audit_iface_names[] =
+  {
+    { "la_activity", true },
+    { "la_objsearch", true },
+    { "la_objopen", true },
+    { "la_preinit", true },
+    { LA_SYMBIND, false },
 #define STRING(s) __STRING (s)
-    "la_" STRING (ARCH_LA_PLTENTER) "\0"
-    "la_" STRING (ARCH_LA_PLTEXIT) "\0"
-    "la_objclose\0";
-  unsigned int cnt = 0;
-  const char *cp = audit_iface_names;
-  do
+    { "la_" STRING (ARCH_LA_PLTENTER), false },
+    { "la_" STRING (ARCH_LA_PLTEXIT), false },
+    { "la_objclose", true },
+  };
+  unsigned int cnt;
+  for (cnt = 0; cnt < naudit_ifaces; cnt++)
     {
-      largs.name = cp;
+      struct audit_ifname *cp = &audit_iface_names[cnt];
+      largs.name = cp->name;
       _dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
 
       /* Store the pointer.  */
       if (err_str == NULL && largs.result != NULL)
-	newp->fptr[cnt] = largs.result;
+        {
+          if (dynload && !cp->dyn_allowed)
+            {
+              _dl_signal_error (EINVAL, name, NULL, N_("\
+unacceptable interfaces for dlload_audit_module()"));
+              unload_audit_module (dlmargs.map, original_tls_idx);
+              return NULL;
+            }
+          else
+	    newp->fptr[cnt] = largs.result;
+	}
       else
 	newp->fptr[cnt] = NULL;
-      ++cnt;
-
-      cp = strchr (cp, '\0') + 1;
     }
-  while (*cp != '\0');
-  assert (cnt == naudit_ifaces);
 
   /* Now append the new auditing interface to the list.  */
   newp->ifaces.next = NULL;
   if (*last_audit == NULL)
-    *last_audit = GLRO(dl_audit) = &newp->ifaces;
+    *last_audit = GL(dl_audit) = &newp->ifaces;
   else
     *last_audit = (*last_audit)->next = &newp->ifaces;
 
   /* The dynamic linker link map is statically allocated, so the
      cookie in _dl_new_object has not happened.  */
-  link_map_audit_state (&GL (dl_rtld_map), GLRO (dl_naudit))->cookie
+  link_map_audit_state (&GL (dl_rtld_map), GL (dl_naudit))->cookie
     = (intptr_t) &GL (dl_rtld_map);
 
-  ++GLRO(dl_naudit);
+  ++GL(dl_naudit);
 
   /* Mark the DSO as being used for auditing.  */
   dlmargs.map->l_auditing = 1;
+  return dlmargs.map;
+}
+
+static void
+load_audit_module (const char *name, struct audit_ifaces **last_audit)
+{
+  if (_load_audit_module (name, dl_main, last_audit, false))
+    {
+      struct link_map *l;
+
+      /* Global audit module can audit all existing objects. */
+      for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l; l = l->l_next)
+        l->l_naudit++;
+    }
+}
+
+static void *
+_dlload_audit_module (const char *name, int flags)
+{
+  struct audit_ifaces *last_audit = GL(dl_audit);
+
+  /* Find last audit list entry. */
+  while (last_audit && last_audit->next)
+    last_audit = last_audit->next;
+  return _load_audit_module (name, _dlload_audit_module, &last_audit, true);
 }
 
 /* Load all audit modules.  */
@@ -1063,7 +1104,7 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
 
   /* Notify audit modules of the initially loaded modules (the main
      program and the dynamic linker itself).  */
-  if (GLRO(dl_naudit) > 0)
+  if (GL(dl_naudit) > 0)
     {
       _dl_audit_objopen (main_map, LM_ID_BASE);
       _dl_audit_objopen (&GL(dl_rtld_map), LM_ID_BASE);
@@ -1822,7 +1863,7 @@ dl_main (const ElfW(Phdr) *phdr,
 
       /* The count based on audit strings may overestimate the number
 	 of audit modules that got loaded, but not underestimate.  */
-      assert (GLRO(dl_naudit) <= naudit);
+      assert (GL(dl_naudit) <= naudit);
     }
 
   /* Keep track of the currently loaded modules to count how many
diff --git a/elf/tst-dynauditmod.c b/elf/tst-dynauditmod.c
new file mode 100644
index 0000000000..46941f7d37
--- /dev/null
+++ b/elf/tst-dynauditmod.c
@@ -0,0 +1,79 @@
+/* Audit mod for dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <link.h>
+#include <assert.h>
+
+/* la_objopen() sets the cookie to this value. Other call-backs check
+ * the value to see if la_objopen() was not somehow skipped. */
+#define TST_COOKIE_VAL 12
+
+unsigned int
+la_version (unsigned int version)
+{
+  fprintf (stderr, "%s\n", __func__);
+  return LAV_CURRENT;
+}
+
+char *
+la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+  return (char *) name;
+}
+
+void
+la_activity (uintptr_t *cookie, unsigned int flag)
+{
+  struct link_map *map = (void *) *cookie;
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+}
+
+unsigned int
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+}
+
+unsigned int
+la_objclose (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    {
+      struct link_map *map = (void *) *cookie;
+      fprintf (stderr, "%s\n", map->l_name);
+    }
+  assert (*cookie == TST_COOKIE_VAL);
+  return 0;
+}
+
+void
+la_preinit (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+}
diff --git a/elf/tst-loadaudit.c b/elf/tst-loadaudit.c
new file mode 100644
index 0000000000..f6db808afb
--- /dev/null
+++ b/elf/tst-loadaudit.c
@@ -0,0 +1,142 @@
+/* Check dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gnu/lib-names.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/support.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+void *
+dlload_audit_module (const char *file, int flags);
+
+static int
+handle_restart (void)
+{
+  void *ah;
+
+  ah = dlload_audit_module ("tst-auditmod18.so", 0);
+  TEST_VERIFY (ah == NULL);
+  fprintf (stderr, "%s\n", dlerror ());
+  ah = dlload_audit_module ("tst-dynauditmod.so", 0);
+  TEST_VERIFY (ah != NULL);
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
+
+    pid_t (*s) (void) = xdlsym (h, "getpid");
+    TEST_COMPARE (s (), getpid ());
+
+    xdlclose (h);
+  }
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, "tst-audit18mod.so", RTLD_NOW);
+
+    int (*foo) (void) = xdlsym (h, "foo");
+    TEST_COMPARE (foo (), 10);
+
+    xdlclose (h);
+  }
+
+  return 0;
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One our fource parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name  */
+
+  if (restart)
+    return handle_restart ();
+
+  char *spargv[9];
+  int i = 0;
+  for (; i < argc - 1; i++)
+    spargv[i] = argv[i + 1];
+  spargv[i++] = (char *) "--direct";
+  spargv[i++] = (char *) "--restart";
+  spargv[i] = NULL;
+
+  struct support_capture_subprocess result
+    = support_capture_subprogram (spargv[0], spargv);
+  support_capture_subprocess_check (&result, "tst-loadaudit", 0, sc_allow_stderr);
+
+  struct
+  {
+    const char *name;
+    bool found;
+  } audit_iface[] =
+  {
+    { "la_version", false },
+    { "la_objsearch", false },
+    { "la_activity", false },
+    { "la_objopen", false },
+    { "la_objclose", false },
+#if __WORDSIZE == 32
+    { "la_symbind32", false },
+#elif __WORDSIZE == 64
+    { "la_symbind64", false },
+#endif
+#define STRING(s) __STRING (s)
+    { "la_" STRING (ARCH_LA_PLTENTER), false },
+    { "la_" STRING (ARCH_LA_PLTEXIT), false },
+  };
+
+  /* Some hooks are called more than once but the test only check if any
+     is called at least once.  */
+  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
+  TEST_VERIFY (out != NULL);
+  char *buffer = NULL;
+  size_t buffer_length = 0;
+  while (xgetline (&buffer, &buffer_length, out))
+    {
+      for (int i = 0; i < array_length (audit_iface); i++)
+	if (strncmp (buffer, audit_iface[i].name,
+		     strlen (audit_iface[i].name)) == 0)
+	  audit_iface[i].found = true;
+    }
+  free (buffer);
+  xfclose (out);
+
+  /* Check that symbols are resolved, except for the last 3.
+     symbind, pltenter, pltexit are not allowed for dynamically loaded mod. */
+  for (int i = 0; i < array_length (audit_iface); i++)
+    TEST_COMPARE (audit_iface[i].found, i < array_length (audit_iface) - 3);
+
+  support_capture_subprocess_free (&result);
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/include/link.h b/include/link.h
index 1d74feb2bd..31f34540fc 100644
--- a/include/link.h
+++ b/include/link.h
@@ -347,6 +347,7 @@ struct link_map
     size_t l_relro_size;
 
     unsigned long long int l_serial;
+    unsigned int l_naudit;
   };
 
 #include <dl-relocate-ld.h>
diff --git a/manual/dynlink.texi b/manual/dynlink.texi
index 6a4a50d3f0..898354ebe9 100644
--- a/manual/dynlink.texi
+++ b/manual/dynlink.texi
@@ -209,6 +209,7 @@ This function is a GNU extension.
 @c dladdr1
 @c dlclose
 @c dlerror
+@c dlload_audit_module
 @c dlmopen
 @c dlopen
 @c dlsym
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c99dad77cc..1a377061f8 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -399,6 +399,10 @@ struct rtld_global
   /* Used to store the audit information for the link map of the
      dynamic loader.  */
   struct auditstate _dl_rtld_auditstate[DL_NNS];
+
+  /* List of auditing interfaces.  */
+  struct audit_ifaces *_dl_audit;
+  unsigned int _dl_naudit;
 #endif
 
 #if !PTHREAD_IN_LIBC && defined SHARED \
@@ -689,12 +693,11 @@ struct rtld_global_ro
      dlopen.  */
   int (*_dl_find_object) (void *, struct dl_find_object *);
 
+  /* Loads audit module.  */
+  void *(*_dlload_audit_module) (const char *name, int flags);
+
   /* 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;
 };
 # define __rtld_global_attribute__
 # if IS_IN (rtld)
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index a0419a13d0..c4618b690d 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2314,6 +2314,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 62e80648e8..6d29418617 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2653,3 +2653,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 9d490fdee8..14376cece0 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2750,6 +2750,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 50874e92fc..e936e68b4f 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2414,3 +2414,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 544b5b2741..ac7f0b6116 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -534,6 +534,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index da532a0191..4452bd3079 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -531,6 +531,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 6c74a60d69..68e22923fc 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2690,3 +2690,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 13d30e646f..f0a1f78c68 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2639,6 +2639,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index f5dfa2a20e..32516113f9 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2823,6 +2823,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 58f1526030..5810db8371 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2588,6 +2588,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index 46ce2437fe..72f140e522 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2174,3 +2174,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index f34085ce35..9ead06cdf0 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -535,6 +535,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 349377d154..c3aee9fa16 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2766,6 +2766,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 38e7fb9b2a..7cb4e492a4 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2739,3 +2739,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index ec4ca27b75..e1d6672d64 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2736,3 +2736,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index bd3f3404fb..3e29d05c1b 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2731,6 +2731,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 9b09fab6ec..36d550962d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2729,6 +2729,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 3b8f2b8ca3..f2773be8d6 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2737,6 +2737,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 497553414d..65d15b6f03 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2639,6 +2639,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index f67f241498..c64625787b 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2778,3 +2778,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index a59a58f44c..8f6e1c7a59 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2160,3 +2160,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index a1bcf79955..e79b46489f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2793,6 +2793,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index c0f28aea45..94dc5ba72c 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2826,6 +2826,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 6b4459964f..9a0401ef37 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2547,6 +2547,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index e90fb502d2..bbdc0d40d3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2849,3 +2849,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index 29be561b60..fad99a0af2 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2416,3 +2416,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 506a4e6a65..1f3e3a81d9 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2616,3 +2616,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 976cd741ee..5ad8f9e052 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2791,6 +2791,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 909ec927dc..3532cb3b68 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2584,6 +2584,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 31a777c4aa..93d7641f76 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2646,6 +2646,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 8d43e8c952..6134cec1e4 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2643,6 +2643,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 91c552dc4c..cda6bb9e29 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2786,6 +2786,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index c5c5e5cf9a..59435bd0b8 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2611,6 +2611,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index e51996e046..c52cbd8aba 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2562,6 +2562,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 388536b3be..f830b889ca 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2668,3 +2668,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127]
  2023-02-26 11:16 [PATCH v4 0/2] minimal run-time audit support Stas Sergeev
@ 2023-02-26 11:16 ` Stas Sergeev
  0 siblings, 0 replies; 8+ messages in thread
From: Stas Sergeev @ 2023-02-26 11:16 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch is an impl of dlload_audit_module() function discussed
in BZ #30127, and the test-case for it, called tst-loadaudit.
It checks the loading of audit module at run-time and makes sure
no "unrecognized" cookie is passed to the module call-backs.

dlload_audit_module (const char *file, int flags)
"file" is a module file name
"flags" are reserved for future use.

As mentioned in BZ #30134, audit module list was in RELRO so this
patch makes it writable. To not compromise the hardening, several
call-backs are disabled for dynamically loaded modules. Namely
symbind, pltenter and pltexit. They have to be disabled also because
the dynamically loaded audit module cannot interract with any object
loaded before it, which is possible in case of these 3 call-backs.

Test-suite run on x86_64 revealed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/Makefile                                |   4 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlaudit.c                               |  62 ++++++
 elf/Makefile                                  |   6 +
 elf/dl-audit.c                                |  47 +++--
 elf/dl-fini.c                                 |   2 +-
 elf/dl-load.c                                 |   7 +-
 elf/dl-object.c                               |   4 +-
 elf/dl-reloc.c                                |   4 +-
 elf/dl-runtime.c                              |   2 +-
 elf/dl-sym-post.h                             |   2 +-
 elf/do-rel.h                                  |   4 +-
 elf/rtld.c                                    |  64 ++++--
 elf/tst-dynauditmod.c                         | 190 ++++++++++++++++++
 elf/tst-loadaudit.c                           | 138 +++++++++++++
 include/link.h                                |   1 +
 manual/dynlink.texi                           |   1 +
 sysdeps/generic/ldsodefs.h                    |  11 +-
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 53 files changed, 535 insertions(+), 52 deletions(-)
 create mode 100644 dlfcn/dlaudit.c
 create mode 100644 elf/tst-dynauditmod.c
 create mode 100644 elf/tst-loadaudit.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..68f6916d0a 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -44,8 +44,8 @@ install-lib-ldscripts = libdl.so
 $(inst_libdir)/libdl.so:
 
 ifeq ($(build-shared),yes)
-routines += dlopenold
-shared-only-routines := dlopenold
+routines += dlopenold dlaudit
+shared-only-routines := dlopenold dlaudit
 endif
 
 ifeq (yes,$(build-shared))
diff --git a/dlfcn/Versions b/dlfcn/Versions
index cc34eb824d..30145316d1 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -28,6 +28,9 @@ libc {
     dlsym;
     dlvsym;
   }
+  GLIBC_2.38 {
+    dlload_audit_module;
+  }
   GLIBC_PRIVATE {
     __libc_dlerror_result;
     _dlerror_run;
diff --git a/dlfcn/dlaudit.c b/dlfcn/dlaudit.c
new file mode 100644
index 0000000000..b124bbf932
--- /dev/null
+++ b/dlfcn/dlaudit.c
@@ -0,0 +1,62 @@
+/* Load an audit module at run time.
+   Copyright (C) 1995-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <shlib-compat.h>
+
+struct dlload_am_args
+{
+  /* The arguments for dlload_am_doit.  */
+  const char *file;
+  int flags;
+  /* The return value of dlload_am_doit.  */
+  void *new;
+};
+
+static void
+dlload_am_doit (void *a)
+{
+  struct dlload_am_args *args = (struct dlload_am_args *) a;
+
+  if (args->flags)
+    _dl_signal_error (0, NULL, NULL, _("invalid flags parameter"));
+
+  args->new = GLRO(dlload_audit_module) (args->file, args->flags);
+}
+
+static void *
+dlload_am_implementation (const char *file, int flags)
+{
+  struct dlload_am_args args;
+  args.file = file;
+  args.flags = flags;
+
+  return _dlerror_run (dlload_am_doit, &args) ? NULL : args.new;
+}
+
+void *
+___dlload_audit_module (const char *file, int flags)
+{
+  return dlload_am_implementation (file, flags);
+}
+versioned_symbol (libc, ___dlload_audit_module, dlload_audit_module,
+                  GLIBC_2_38);
diff --git a/elf/Makefile b/elf/Makefile
index 0d19964d42..863c459bdc 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -822,6 +822,7 @@ modules-names += \
   tst-deep1mod1 \
   tst-deep1mod2 \
   tst-deep1mod3 \
+  tst-dynauditmod \
   tst-dl_find_object-mod1 \
   tst-dl_find_object-mod2 \
   tst-dl_find_object-mod3 \
@@ -1058,6 +1059,7 @@ ifeq (yes,$(build-shared))
 tests += \
   tst-ifunc-fault-bindnow \
   tst-ifunc-fault-lazy \
+  tst-loadaudit \
   # tests
 # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
 tests-internal += \
@@ -2352,6 +2354,10 @@ $(objpfx)tst-audit28.out: $(objpfx)tst-auditmod28.so
 $(objpfx)tst-auditmod28.so: $(libsupport)
 tst-audit28-ENV = LD_AUDIT=$(objpfx)tst-auditmod28.so
 
+$(objpfx)tst-loadaudit.out: $(objpfx)tst-dynauditmod.so \
+			  $(objpfx)tst-audit18mod.so
+tst-loadaudit-ARGS = -- $(host-test-program-cmd)
+
 # tst-sonamemove links against an older implementation of the library.
 LDFLAGS-tst-sonamemove-linkmod1.so = \
   -Wl,--version-script=tst-sonamemove-linkmod1.map \
diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index 00e794aa26..0770bd52b2 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -27,8 +27,8 @@
 void
 _dl_audit_activity_map (struct link_map *l, int action)
 {
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->activity != NULL)
 	afct->activity (&link_map_audit_state (l, cnt)->cookie, action);
@@ -43,8 +43,7 @@ _dl_audit_activity_nsid (Lmid_t nsid, int action)
      does not give us a way to signal LA_ACT_CONSISTENT for it because the
      first loaded module is used to identify the namespace.  */
   struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
-      || head == NULL || head->l_auditing)
+  if (__glibc_likely (head == NULL || head->l_naudit == 0 || head->l_auditing))
     return;
 
   _dl_audit_activity_map (head, action);
@@ -56,8 +55,8 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
   if (l == NULL || l->l_auditing || code == 0)
     return name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objsearch != NULL)
 	{
@@ -75,11 +74,11 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
 void
 _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objopen != NULL)
 	{
@@ -95,12 +94,12 @@ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 void
 _dl_audit_objclose (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
+  if (__glibc_likely (l->l_naudit == 0)
       || GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objclose != NULL)
 	{
@@ -116,11 +115,11 @@ _dl_audit_objclose (struct link_map *l)
 void
 _dl_audit_preinit (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->preinit != NULL)
 	afct->preinit (&link_map_audit_state (l, cnt)->cookie);
@@ -145,8 +144,8 @@ _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref, void **value,
   ElfW(Sym) sym = *ref;
   sym.st_value = (ElfW(Addr)) *value;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       struct auditstate *match_audit = link_map_audit_state (l, cnt);
       struct auditstate *result_audit = link_map_audit_state (result, cnt);
@@ -212,9 +211,9 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
   const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
 
   unsigned int flags = 0;
-  struct audit_ifaces *afct = GLRO(dl_audit);
+  struct audit_ifaces *afct = GL(dl_audit);
   uintptr_t new_value = (uintptr_t) sym.st_value;
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       /* XXX Check whether both DSOs must request action or only one */
       struct auditstate *l_state = link_map_audit_state (l, cnt);
@@ -267,7 +266,7 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
 		    DL_FIXUP_VALUE_TYPE *value, void *regs, long int *framesize)
 {
   /* Don't do anything if no auditor wants to intercept this call.  */
-  if (GLRO(dl_naudit) == 0
+  if (l->l_naudit == 0
       || (reloc_result->enterexit & LA_SYMB_NOPLTENTER))
     return;
 
@@ -290,8 +289,8 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
   /* Keep track of overwritten addresses.  */
   unsigned int flags = reloc_result->flags;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTENTER != NULL
 	  && (reloc_result->enterexit
@@ -363,8 +362,8 @@ _dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
 					     l_info[DT_STRTAB]);
   const char *symname = strtab + sym.st_name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTEXIT != NULL
 	  && (reloc_result->enterexit
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 9acb64f47c..a96f769953 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -129,7 +129,7 @@ _dl_fini (void)
     }
 
 #ifdef SHARED
-  if (! do_audit && GLRO(dl_naudit) > 0)
+  if (! do_audit && GL(dl_naudit) > 0)
     {
       do_audit = 1;
       goto again;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fcb39a78d4..720324ccfb 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1032,6 +1032,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
       l->l_addr = l->l_real->l_addr;
       l->l_ld = l->l_real->l_ld;
 
+      /* Do not call audit when this lm closes. */
+      l->l_naudit = 0;
+
       /* No need to bump the refcount of the real object, ld.so will
 	 never be unloaded.  */
       __close_nocancel (fd);
@@ -1607,7 +1610,7 @@ open_verify (const char *name, int fd,
 
 #ifdef SHARED
   /* Give the auditing libraries a chance.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *original_name = name;
       name = _dl_audit_objsearch (name, loader, whatcode);
@@ -2001,7 +2004,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 #ifdef SHARED
   /* Give the auditing libraries a chance to change the name before we
      try anything.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *before = name;
       name = _dl_audit_objsearch (name, loader, LA_SER_ORIG);
diff --git a/elf/dl-object.c b/elf/dl-object.c
index f1f2ec956c..497c57154a 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -77,7 +77,7 @@ _dl_new_object (char *realname, const char *libname, int type,
       naudit = DL_NNS;
     }
   else
-    naudit = GLRO (dl_naudit);
+    naudit = GL (dl_naudit);
 #endif
 
   size_t libname_len = strlen (libname) + 1;
@@ -136,6 +136,8 @@ _dl_new_object (char *realname, const char *libname, int type,
   new->l_ns = nsid;
 
 #ifdef SHARED
+  /* GL(dl_naudit) is a current value and naudit is maximum value. */
+  new->l_naudit = GL (dl_naudit);
   for (unsigned int cnt = 0; cnt < naudit; ++cnt)
     /* No need to initialize bindflags due to calloc.  */
     link_map_audit_state (new, cnt)->cookie = (uintptr_t) new;
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 1d558c1e0c..8aedbc0d96 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -222,8 +222,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
   /* If we are auditing, install the same handlers we need for profiling.  */
   if ((reloc_mode & __RTLD_AUDIT) == 0)
     {
-      struct audit_ifaces *afct = GLRO(dl_audit);
-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+      struct audit_ifaces *afct = GL(dl_audit);
+      for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
 	{
 	  /* Profiling is needed only if PLT hooks are provided.  */
 	  if (afct->ARCH_LA_PLTENTER != NULL
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index d35a725415..672d6fb702 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -313,7 +313,7 @@ _dl_profile_fixup (
       /* Auditing checkpoint: we have a new binding.  Provide the
 	 auditing libraries the possibility to change the value and
 	 tell us whether further auditing is wanted.  */
-      if (defsym != NULL && GLRO(dl_naudit) > 0)
+      if (defsym != NULL && GL(dl_naudit) > 0)
 	_dl_audit_symbind (l, reloc_result, defsym, &value, result);
 #endif
 
diff --git a/elf/dl-sym-post.h b/elf/dl-sym-post.h
index 5623d63ac8..0cc50349ca 100644
--- a/elf/dl-sym-post.h
+++ b/elf/dl-sym-post.h
@@ -50,7 +50,7 @@ _dl_sym_post (lookup_t result, const ElfW(Sym) *ref, void *value,
   /* Auditing checkpoint: we have a new binding.  Provide the
      auditing libraries the possibility to change the value and
      tell us whether further auditing is wanted.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       if (match == NULL)
         match = _dl_sym_find_caller_link_map (caller);
diff --git a/elf/do-rel.h b/elf/do-rel.h
index 7e1cc4452a..22b5d566eb 100644
--- a/elf/do-rel.h
+++ b/elf/do-rel.h
@@ -148,7 +148,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 #if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym, rversion,
@@ -193,7 +193,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 # if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym,
diff --git a/elf/rtld.c b/elf/rtld.c
index f82fbeb132..863b5a500b 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -151,6 +151,10 @@ static const char *audit_list_next (struct audit_list *);
 /* Initialize *STATE with the defaults.  */
 static void dl_main_state_init (struct dl_main_state *state);
 
+/* Loads audit module. */
+static void *
+_dlload_audit_module (const char *name, int flags);
+
 /* Process all environments variables the dynamic linker must recognize.
    Since all of them start with `LD_' we are a bit smarter while finding
    all the entries.  */
@@ -272,7 +276,7 @@ audit_list_next (struct audit_list *list)
     }
 }
 
-/* Count audit modules before they are loaded so GLRO(dl_naudit)
+/* Count audit modules before they are loaded so GL(dl_naudit)
    is not yet usable.  */
 static size_t
 audit_list_count (struct audit_list *list)
@@ -375,6 +379,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_error_free = _dl_error_free,
     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
     ._dl_libc_freeres = __rtld_libc_freeres,
+    ._dlload_audit_module = _dlload_audit_module,
   };
 /* If we would use strong_alias here the compiler would see a
    non-hidden definition.  This would undo the effect of the previous
@@ -930,8 +935,9 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 }
 
 /* Load one audit module.  */
-static void
-load_audit_module (const char *name, struct audit_ifaces **last_audit)
+static void *
+_load_audit_module (const char *name, const void *caller_dlopen,
+                   struct audit_ifaces **last_audit)
 {
   int original_tls_idx = GL(dl_tls_max_dtv_idx);
 
@@ -946,7 +952,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
   if (__glibc_unlikely (err_str != NULL))
     {
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   struct lookup_args largs;
@@ -957,7 +963,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
     {
       unload_audit_module (dlmargs.map, original_tls_idx);
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   unsigned int (*laversion) (unsigned int) = largs.result;
@@ -977,7 +983,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
 file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 			  dlmargs.map->l_name, dlmargs.map->l_ns);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   if (!_dl_audit_check_version (lav))
@@ -986,7 +992,7 @@ file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
 			name, lav, LAV_CURRENT);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   enum { naudit_ifaces = 8 };
@@ -1032,19 +1038,53 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
   /* Now append the new auditing interface to the list.  */
   newp->ifaces.next = NULL;
   if (*last_audit == NULL)
-    *last_audit = GLRO(dl_audit) = &newp->ifaces;
+    *last_audit = GL(dl_audit) = &newp->ifaces;
   else
     *last_audit = (*last_audit)->next = &newp->ifaces;
 
   /* The dynamic linker link map is statically allocated, so the
      cookie in _dl_new_object has not happened.  */
-  link_map_audit_state (&GL (dl_rtld_map), GLRO (dl_naudit))->cookie
+  link_map_audit_state (&GL (dl_rtld_map), GL (dl_naudit))->cookie
     = (intptr_t) &GL (dl_rtld_map);
 
-  ++GLRO(dl_naudit);
+  ++GL(dl_naudit);
 
   /* Mark the DSO as being used for auditing.  */
   dlmargs.map->l_auditing = 1;
+  return dlmargs.map;
+}
+
+static void
+load_audit_module (const char *name, struct audit_ifaces **last_audit)
+{
+  if (_load_audit_module (name, dl_main, last_audit))
+    {
+      struct link_map *l;
+
+      /* Global audit module can audit all existing objects. */
+      for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l; l = l->l_next)
+        l->l_naudit++;
+    }
+}
+
+static void *
+_dlload_audit_module (const char *name, int flags)
+{
+  struct link_map *l;
+  struct audit_ifaces *last_audit = GL(dl_audit);
+
+  /* Find last audit list entry. */
+  while (last_audit && last_audit->next)
+    last_audit = last_audit->next;
+  l = _load_audit_module (name, _dlload_audit_module, &last_audit);
+  if (l)
+    {
+      /* These are not allowed for dynamically-loaded auditors. */
+      last_audit->symbind = NULL;
+      last_audit->ARCH_LA_PLTENTER = NULL;
+      last_audit->ARCH_LA_PLTEXIT = NULL;
+    }
+  return l;
 }
 
 /* Load all audit modules.  */
@@ -1063,7 +1103,7 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
 
   /* Notify audit modules of the initially loaded modules (the main
      program and the dynamic linker itself).  */
-  if (GLRO(dl_naudit) > 0)
+  if (GL(dl_naudit) > 0)
     {
       _dl_audit_objopen (main_map, LM_ID_BASE);
       _dl_audit_objopen (&GL(dl_rtld_map), LM_ID_BASE);
@@ -1822,7 +1862,7 @@ dl_main (const ElfW(Phdr) *phdr,
 
       /* The count based on audit strings may overestimate the number
 	 of audit modules that got loaded, but not underestimate.  */
-      assert (GLRO(dl_naudit) <= naudit);
+      assert (GL(dl_naudit) <= naudit);
     }
 
   /* Keep track of the currently loaded modules to count how many
diff --git a/elf/tst-dynauditmod.c b/elf/tst-dynauditmod.c
new file mode 100644
index 0000000000..322e9ae4c7
--- /dev/null
+++ b/elf/tst-dynauditmod.c
@@ -0,0 +1,190 @@
+/* Audit mod for dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <link.h>
+#include <assert.h>
+
+/* la_objopen() sets the cookie to this value. Other call-backs check
+ * the value to see if la_objopen() was not somehow skipped. */
+#define TST_COOKIE_VAL 12
+
+unsigned int
+la_version (unsigned int version)
+{
+  fprintf (stderr, "%s\n", __func__);
+  return LAV_CURRENT;
+}
+
+char *
+la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+  return (char *) name;
+}
+
+void
+la_activity (uintptr_t *cookie, unsigned int flag)
+{
+  struct link_map *map = (void *) *cookie;
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+}
+
+unsigned int
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+}
+
+unsigned int
+la_objclose (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    {
+      struct link_map *map = (void *) *cookie;
+      fprintf (stderr, "%s\n", map->l_name);
+    }
+  assert (*cookie == TST_COOKIE_VAL);
+  return 0;
+}
+
+void
+la_preinit (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+}
+
+uintptr_t
+#if __ELF_NATIVE_CLASS == 32
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+              uintptr_t *defcook, unsigned int *flags, const char *symname)
+#else
+la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+              uintptr_t *defcook, unsigned int *flags, const char *symname)
+#endif
+{
+  fprintf (stderr, "%s\n", __func__);
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+
+#ifdef __i386__
+Elf32_Addr
+la_i86_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)),
+		     unsigned int ndx __attribute__ ((unused)),
+		     uintptr_t *refcook, uintptr_t *defcook,
+		     La_i86_regs *regs, unsigned int *flags,
+		     const char *symname, long int *framesizep)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+#elif defined __x86_64__
+Elf64_Addr
+la_x86_64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)),
+			unsigned int ndx __attribute__ ((unused)),
+			uintptr_t *refcook, uintptr_t *defcook,
+			La_x86_64_regs *regs, unsigned int *flags,
+			const char *symname, long int *framesizep)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+#elif defined __sparc__ && !defined __arch64__
+Elf32_Addr
+la_sparc32_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)),
+			 unsigned int ndx __attribute__ ((unused)),
+			 uintptr_t *refcook, uintptr_t *defcook,
+			 La_sparc32_regs *regs, unsigned int *flags,
+			 const char *symname, long int *framesizep)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+#elif defined __sparc__ && defined __arch64__
+Elf64_Addr
+la_sparc64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)),
+			 unsigned int ndx __attribute__ ((unused)),
+			 uintptr_t *refcook, uintptr_t *defcook,
+			 La_sparc64_regs *regs, unsigned int *flags,
+			 const char *symname, long int *framesizep)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+#elif !defined HAVE_ARCH_PLTENTER
+# warning "pltenter for architecture not supported"
+#endif
+
+#ifdef __i386__
+unsigned int
+la_i86_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+		    uintptr_t *defcook, const struct La_i86_regs *inregs,
+		    struct La_i86_retval *outregs, const char *symname)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return 0;
+}
+#elif defined __x86_64__
+unsigned int
+la_x86_64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+		       uintptr_t *defcook, const struct La_x86_64_regs *inregs,
+		       struct La_x86_64_retval *outregs, const char *symname)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return 0;
+}
+#elif defined __sparc__ && !defined __arch64__
+unsigned int
+la_sparc32_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+			uintptr_t *defcook, const struct La_sparc32_regs *inregs,
+			struct La_sparc32_retval *outregs, const char *symname)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return 0;
+}
+#elif defined __sparc__ && defined __arch64__
+unsigned int
+la_sparc64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+			uintptr_t *defcook, const struct La_sparc64_regs *inregs,
+			struct La_sparc64_retval *outregs, const char *symname)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return 0;
+}
+#elif !defined HAVE_ARCH_PLTEXIT
+# warning "pltexit for architecture not supported"
+#endif
diff --git a/elf/tst-loadaudit.c b/elf/tst-loadaudit.c
new file mode 100644
index 0000000000..4bf86d06a8
--- /dev/null
+++ b/elf/tst-loadaudit.c
@@ -0,0 +1,138 @@
+/* Check dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gnu/lib-names.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/support.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+void *
+dlload_audit_module (const char *file, int flags);
+
+static int
+handle_restart (void)
+{
+  void *ah = dlload_audit_module ("tst-dynauditmod.so", 0);
+
+  TEST_VERIFY (ah != NULL);
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
+
+    pid_t (*s) (void) = xdlsym (h, "getpid");
+    TEST_COMPARE (s (), getpid ());
+
+    xdlclose (h);
+  }
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, "tst-audit18mod.so", RTLD_NOW);
+
+    int (*foo) (void) = xdlsym (h, "foo");
+    TEST_COMPARE (foo (), 10);
+
+    xdlclose (h);
+  }
+
+  return 0;
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One our fource parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name  */
+
+  if (restart)
+    return handle_restart ();
+
+  char *spargv[9];
+  int i = 0;
+  for (; i < argc - 1; i++)
+    spargv[i] = argv[i + 1];
+  spargv[i++] = (char *) "--direct";
+  spargv[i++] = (char *) "--restart";
+  spargv[i] = NULL;
+
+  struct support_capture_subprocess result
+    = support_capture_subprogram (spargv[0], spargv);
+  support_capture_subprocess_check (&result, "tst-loadaudit", 0, sc_allow_stderr);
+
+  struct
+  {
+    const char *name;
+    bool found;
+  } audit_iface[] =
+  {
+    { "la_version", false },
+    { "la_objsearch", false },
+    { "la_activity", false },
+    { "la_objopen", false },
+    { "la_objclose", false },
+#if __WORDSIZE == 32
+    { "la_symbind32", false },
+#elif __WORDSIZE == 64
+    { "la_symbind64", false },
+#endif
+#define STRING(s) __STRING (s)
+    { "la_" STRING (ARCH_LA_PLTENTER), false },
+    { "la_" STRING (ARCH_LA_PLTEXIT), false },
+  };
+
+  /* Some hooks are called more than once but the test only check if any
+     is called at least once.  */
+  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
+  TEST_VERIFY (out != NULL);
+  char *buffer = NULL;
+  size_t buffer_length = 0;
+  while (xgetline (&buffer, &buffer_length, out))
+    {
+      for (int i = 0; i < array_length (audit_iface); i++)
+	if (strncmp (buffer, audit_iface[i].name,
+		     strlen (audit_iface[i].name)) == 0)
+	  audit_iface[i].found = true;
+    }
+  free (buffer);
+  xfclose (out);
+
+  /* Check that symbols are resolved, except for the last 3.
+     symbind, pltenter, pltexit are not allowed for dynamically loaded mod. */
+  for (int i = 0; i < array_length (audit_iface); i++)
+    TEST_COMPARE (audit_iface[i].found, i < array_length (audit_iface) - 3);
+
+  support_capture_subprocess_free (&result);
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/include/link.h b/include/link.h
index 1d74feb2bd..31f34540fc 100644
--- a/include/link.h
+++ b/include/link.h
@@ -347,6 +347,7 @@ struct link_map
     size_t l_relro_size;
 
     unsigned long long int l_serial;
+    unsigned int l_naudit;
   };
 
 #include <dl-relocate-ld.h>
diff --git a/manual/dynlink.texi b/manual/dynlink.texi
index 6a4a50d3f0..898354ebe9 100644
--- a/manual/dynlink.texi
+++ b/manual/dynlink.texi
@@ -209,6 +209,7 @@ This function is a GNU extension.
 @c dladdr1
 @c dlclose
 @c dlerror
+@c dlload_audit_module
 @c dlmopen
 @c dlopen
 @c dlsym
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c99dad77cc..1a377061f8 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -399,6 +399,10 @@ struct rtld_global
   /* Used to store the audit information for the link map of the
      dynamic loader.  */
   struct auditstate _dl_rtld_auditstate[DL_NNS];
+
+  /* List of auditing interfaces.  */
+  struct audit_ifaces *_dl_audit;
+  unsigned int _dl_naudit;
 #endif
 
 #if !PTHREAD_IN_LIBC && defined SHARED \
@@ -689,12 +693,11 @@ struct rtld_global_ro
      dlopen.  */
   int (*_dl_find_object) (void *, struct dl_find_object *);
 
+  /* Loads audit module.  */
+  void *(*_dlload_audit_module) (const char *name, int flags);
+
   /* 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;
 };
 # define __rtld_global_attribute__
 # if IS_IN (rtld)
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index a0419a13d0..c4618b690d 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2314,6 +2314,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 62e80648e8..6d29418617 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2653,3 +2653,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 9d490fdee8..14376cece0 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2750,6 +2750,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 50874e92fc..e936e68b4f 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2414,3 +2414,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 544b5b2741..ac7f0b6116 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -534,6 +534,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index da532a0191..4452bd3079 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -531,6 +531,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 6c74a60d69..68e22923fc 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2690,3 +2690,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 13d30e646f..f0a1f78c68 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2639,6 +2639,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index f5dfa2a20e..32516113f9 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2823,6 +2823,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 58f1526030..5810db8371 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2588,6 +2588,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index 46ce2437fe..72f140e522 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2174,3 +2174,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index f34085ce35..9ead06cdf0 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -535,6 +535,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 349377d154..c3aee9fa16 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2766,6 +2766,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 38e7fb9b2a..7cb4e492a4 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2739,3 +2739,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index ec4ca27b75..e1d6672d64 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2736,3 +2736,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index bd3f3404fb..3e29d05c1b 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2731,6 +2731,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 9b09fab6ec..36d550962d 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2729,6 +2729,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 3b8f2b8ca3..f2773be8d6 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2737,6 +2737,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 497553414d..65d15b6f03 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2639,6 +2639,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index f67f241498..c64625787b 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2778,3 +2778,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index a59a58f44c..8f6e1c7a59 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2160,3 +2160,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index a1bcf79955..e79b46489f 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2793,6 +2793,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index c0f28aea45..94dc5ba72c 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2826,6 +2826,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 6b4459964f..9a0401ef37 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2547,6 +2547,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index e90fb502d2..bbdc0d40d3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2849,3 +2849,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index 29be561b60..fad99a0af2 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2416,3 +2416,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 506a4e6a65..1f3e3a81d9 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2616,3 +2616,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 976cd741ee..5ad8f9e052 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2791,6 +2791,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 909ec927dc..3532cb3b68 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2584,6 +2584,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 31a777c4aa..93d7641f76 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2646,6 +2646,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 8d43e8c952..6134cec1e4 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2643,6 +2643,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 91c552dc4c..cda6bb9e29 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2786,6 +2786,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index c5c5e5cf9a..59435bd0b8 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2611,6 +2611,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index e51996e046..c52cbd8aba 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2562,6 +2562,7 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 388536b3be..f830b889ca 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2668,3 +2668,4 @@ GLIBC_2.38 __isoc23_wcstoul_l F
 GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
+GLIBC_2.38 dlload_audit_module F
-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127]
  2023-02-24 16:53 [PATCH v3 0/2] minimal run-time audit support Stas Sergeev
@ 2023-02-24 16:54 ` Stas Sergeev
  0 siblings, 0 replies; 8+ messages in thread
From: Stas Sergeev @ 2023-02-24 16:54 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch is an impl of dlload_audit_module() function discussed
in BZ #30127, and the test-case for it, called tst-loadaudit.
It checks the loading of audit module at run-time and makes sure
no "unrecognized" cookie is passed to the module call-backs.

dlload_audit_module (const char *file, int flags)
"file" is a module file name
"flags" are reserved for future use.

As mentioned in BZ #30134, audit module list was in RELRO so this
patch makes it writable. To not compromise the hardening, several
call-backs are disabled for dynamically loaded modules. Namely
symbind, pltenter and pltexit. They have to be disabled also because
the dynamically loaded audit module cannot interract with any object
loaded before it, which is possible in case of these 3 call-backs.

Test-suite run on x86_64 revealed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/Makefile                                |   4 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlaudit.c                               |  62 ++++++
 elf/Makefile                                  |   6 +
 elf/dl-audit.c                                |  47 +++--
 elf/dl-fini.c                                 |   2 +-
 elf/dl-load.c                                 |   7 +-
 elf/dl-object.c                               |   4 +-
 elf/dl-reloc.c                                |   4 +-
 elf/dl-runtime.c                              |   2 +-
 elf/dl-sym-post.h                             |   2 +-
 elf/do-rel.h                                  |   4 +-
 elf/rtld.c                                    |  64 ++++--
 elf/tst-dynauditmod.c                         | 190 ++++++++++++++++++
 elf/tst-loadaudit.c                           | 138 +++++++++++++
 include/link.h                                |   1 +
 manual/dynlink.texi                           |   1 +
 sysdeps/generic/ldsodefs.h                    |  11 +-
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 53 files changed, 535 insertions(+), 52 deletions(-)
 create mode 100644 dlfcn/dlaudit.c
 create mode 100644 elf/tst-dynauditmod.c
 create mode 100644 elf/tst-loadaudit.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..68f6916d0a 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -44,8 +44,8 @@ install-lib-ldscripts = libdl.so
 $(inst_libdir)/libdl.so:
 
 ifeq ($(build-shared),yes)
-routines += dlopenold
-shared-only-routines := dlopenold
+routines += dlopenold dlaudit
+shared-only-routines := dlopenold dlaudit
 endif
 
 ifeq (yes,$(build-shared))
diff --git a/dlfcn/Versions b/dlfcn/Versions
index cc34eb824d..30145316d1 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -28,6 +28,9 @@ libc {
     dlsym;
     dlvsym;
   }
+  GLIBC_2.38 {
+    dlload_audit_module;
+  }
   GLIBC_PRIVATE {
     __libc_dlerror_result;
     _dlerror_run;
diff --git a/dlfcn/dlaudit.c b/dlfcn/dlaudit.c
new file mode 100644
index 0000000000..b124bbf932
--- /dev/null
+++ b/dlfcn/dlaudit.c
@@ -0,0 +1,62 @@
+/* Load an audit module at run time.
+   Copyright (C) 1995-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <shlib-compat.h>
+
+struct dlload_am_args
+{
+  /* The arguments for dlload_am_doit.  */
+  const char *file;
+  int flags;
+  /* The return value of dlload_am_doit.  */
+  void *new;
+};
+
+static void
+dlload_am_doit (void *a)
+{
+  struct dlload_am_args *args = (struct dlload_am_args *) a;
+
+  if (args->flags)
+    _dl_signal_error (0, NULL, NULL, _("invalid flags parameter"));
+
+  args->new = GLRO(dlload_audit_module) (args->file, args->flags);
+}
+
+static void *
+dlload_am_implementation (const char *file, int flags)
+{
+  struct dlload_am_args args;
+  args.file = file;
+  args.flags = flags;
+
+  return _dlerror_run (dlload_am_doit, &args) ? NULL : args.new;
+}
+
+void *
+___dlload_audit_module (const char *file, int flags)
+{
+  return dlload_am_implementation (file, flags);
+}
+versioned_symbol (libc, ___dlload_audit_module, dlload_audit_module,
+                  GLIBC_2_38);
diff --git a/elf/Makefile b/elf/Makefile
index 2fc6391183..c73103c84a 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -821,6 +821,7 @@ modules-names += \
   tst-deep1mod1 \
   tst-deep1mod2 \
   tst-deep1mod3 \
+  tst-dynauditmod \
   tst-dl_find_object-mod1 \
   tst-dl_find_object-mod2 \
   tst-dl_find_object-mod3 \
@@ -1055,6 +1056,7 @@ ifeq (yes,$(build-shared))
 tests += \
   tst-ifunc-fault-bindnow \
   tst-ifunc-fault-lazy \
+  tst-loadaudit \
   # tests
 # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
 tests-internal += \
@@ -2349,6 +2351,10 @@ $(objpfx)tst-audit28.out: $(objpfx)tst-auditmod28.so
 $(objpfx)tst-auditmod28.so: $(libsupport)
 tst-audit28-ENV = LD_AUDIT=$(objpfx)tst-auditmod28.so
 
+$(objpfx)tst-loadaudit.out: $(objpfx)tst-dynauditmod.so \
+			  $(objpfx)tst-audit18mod.so
+tst-loadaudit-ARGS = -- $(host-test-program-cmd)
+
 # tst-sonamemove links against an older implementation of the library.
 LDFLAGS-tst-sonamemove-linkmod1.so = \
   -Wl,--version-script=tst-sonamemove-linkmod1.map \
diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index 00e794aa26..0770bd52b2 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -27,8 +27,8 @@
 void
 _dl_audit_activity_map (struct link_map *l, int action)
 {
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->activity != NULL)
 	afct->activity (&link_map_audit_state (l, cnt)->cookie, action);
@@ -43,8 +43,7 @@ _dl_audit_activity_nsid (Lmid_t nsid, int action)
      does not give us a way to signal LA_ACT_CONSISTENT for it because the
      first loaded module is used to identify the namespace.  */
   struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
-      || head == NULL || head->l_auditing)
+  if (__glibc_likely (head == NULL || head->l_naudit == 0 || head->l_auditing))
     return;
 
   _dl_audit_activity_map (head, action);
@@ -56,8 +55,8 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
   if (l == NULL || l->l_auditing || code == 0)
     return name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objsearch != NULL)
 	{
@@ -75,11 +74,11 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
 void
 _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objopen != NULL)
 	{
@@ -95,12 +94,12 @@ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 void
 _dl_audit_objclose (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
+  if (__glibc_likely (l->l_naudit == 0)
       || GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objclose != NULL)
 	{
@@ -116,11 +115,11 @@ _dl_audit_objclose (struct link_map *l)
 void
 _dl_audit_preinit (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->preinit != NULL)
 	afct->preinit (&link_map_audit_state (l, cnt)->cookie);
@@ -145,8 +144,8 @@ _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref, void **value,
   ElfW(Sym) sym = *ref;
   sym.st_value = (ElfW(Addr)) *value;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       struct auditstate *match_audit = link_map_audit_state (l, cnt);
       struct auditstate *result_audit = link_map_audit_state (result, cnt);
@@ -212,9 +211,9 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
   const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
 
   unsigned int flags = 0;
-  struct audit_ifaces *afct = GLRO(dl_audit);
+  struct audit_ifaces *afct = GL(dl_audit);
   uintptr_t new_value = (uintptr_t) sym.st_value;
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       /* XXX Check whether both DSOs must request action or only one */
       struct auditstate *l_state = link_map_audit_state (l, cnt);
@@ -267,7 +266,7 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
 		    DL_FIXUP_VALUE_TYPE *value, void *regs, long int *framesize)
 {
   /* Don't do anything if no auditor wants to intercept this call.  */
-  if (GLRO(dl_naudit) == 0
+  if (l->l_naudit == 0
       || (reloc_result->enterexit & LA_SYMB_NOPLTENTER))
     return;
 
@@ -290,8 +289,8 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
   /* Keep track of overwritten addresses.  */
   unsigned int flags = reloc_result->flags;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTENTER != NULL
 	  && (reloc_result->enterexit
@@ -363,8 +362,8 @@ _dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
 					     l_info[DT_STRTAB]);
   const char *symname = strtab + sym.st_name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTEXIT != NULL
 	  && (reloc_result->enterexit
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 9acb64f47c..a96f769953 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -129,7 +129,7 @@ _dl_fini (void)
     }
 
 #ifdef SHARED
-  if (! do_audit && GLRO(dl_naudit) > 0)
+  if (! do_audit && GL(dl_naudit) > 0)
     {
       do_audit = 1;
       goto again;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fcb39a78d4..720324ccfb 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1032,6 +1032,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
       l->l_addr = l->l_real->l_addr;
       l->l_ld = l->l_real->l_ld;
 
+      /* Do not call audit when this lm closes. */
+      l->l_naudit = 0;
+
       /* No need to bump the refcount of the real object, ld.so will
 	 never be unloaded.  */
       __close_nocancel (fd);
@@ -1607,7 +1610,7 @@ open_verify (const char *name, int fd,
 
 #ifdef SHARED
   /* Give the auditing libraries a chance.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *original_name = name;
       name = _dl_audit_objsearch (name, loader, whatcode);
@@ -2001,7 +2004,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 #ifdef SHARED
   /* Give the auditing libraries a chance to change the name before we
      try anything.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *before = name;
       name = _dl_audit_objsearch (name, loader, LA_SER_ORIG);
diff --git a/elf/dl-object.c b/elf/dl-object.c
index f1f2ec956c..497c57154a 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -77,7 +77,7 @@ _dl_new_object (char *realname, const char *libname, int type,
       naudit = DL_NNS;
     }
   else
-    naudit = GLRO (dl_naudit);
+    naudit = GL (dl_naudit);
 #endif
 
   size_t libname_len = strlen (libname) + 1;
@@ -136,6 +136,8 @@ _dl_new_object (char *realname, const char *libname, int type,
   new->l_ns = nsid;
 
 #ifdef SHARED
+  /* GL(dl_naudit) is a current value and naudit is maximum value. */
+  new->l_naudit = GL (dl_naudit);
   for (unsigned int cnt = 0; cnt < naudit; ++cnt)
     /* No need to initialize bindflags due to calloc.  */
     link_map_audit_state (new, cnt)->cookie = (uintptr_t) new;
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 1d558c1e0c..8aedbc0d96 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -222,8 +222,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
   /* If we are auditing, install the same handlers we need for profiling.  */
   if ((reloc_mode & __RTLD_AUDIT) == 0)
     {
-      struct audit_ifaces *afct = GLRO(dl_audit);
-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+      struct audit_ifaces *afct = GL(dl_audit);
+      for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
 	{
 	  /* Profiling is needed only if PLT hooks are provided.  */
 	  if (afct->ARCH_LA_PLTENTER != NULL
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index d35a725415..672d6fb702 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -313,7 +313,7 @@ _dl_profile_fixup (
       /* Auditing checkpoint: we have a new binding.  Provide the
 	 auditing libraries the possibility to change the value and
 	 tell us whether further auditing is wanted.  */
-      if (defsym != NULL && GLRO(dl_naudit) > 0)
+      if (defsym != NULL && GL(dl_naudit) > 0)
 	_dl_audit_symbind (l, reloc_result, defsym, &value, result);
 #endif
 
diff --git a/elf/dl-sym-post.h b/elf/dl-sym-post.h
index 5623d63ac8..0cc50349ca 100644
--- a/elf/dl-sym-post.h
+++ b/elf/dl-sym-post.h
@@ -50,7 +50,7 @@ _dl_sym_post (lookup_t result, const ElfW(Sym) *ref, void *value,
   /* Auditing checkpoint: we have a new binding.  Provide the
      auditing libraries the possibility to change the value and
      tell us whether further auditing is wanted.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       if (match == NULL)
         match = _dl_sym_find_caller_link_map (caller);
diff --git a/elf/do-rel.h b/elf/do-rel.h
index 7e1cc4452a..22b5d566eb 100644
--- a/elf/do-rel.h
+++ b/elf/do-rel.h
@@ -148,7 +148,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 #if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym, rversion,
@@ -193,7 +193,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 # if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym,
diff --git a/elf/rtld.c b/elf/rtld.c
index f82fbeb132..863b5a500b 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -151,6 +151,10 @@ static const char *audit_list_next (struct audit_list *);
 /* Initialize *STATE with the defaults.  */
 static void dl_main_state_init (struct dl_main_state *state);
 
+/* Loads audit module. */
+static void *
+_dlload_audit_module (const char *name, int flags);
+
 /* Process all environments variables the dynamic linker must recognize.
    Since all of them start with `LD_' we are a bit smarter while finding
    all the entries.  */
@@ -272,7 +276,7 @@ audit_list_next (struct audit_list *list)
     }
 }
 
-/* Count audit modules before they are loaded so GLRO(dl_naudit)
+/* Count audit modules before they are loaded so GL(dl_naudit)
    is not yet usable.  */
 static size_t
 audit_list_count (struct audit_list *list)
@@ -375,6 +379,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_error_free = _dl_error_free,
     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
     ._dl_libc_freeres = __rtld_libc_freeres,
+    ._dlload_audit_module = _dlload_audit_module,
   };
 /* If we would use strong_alias here the compiler would see a
    non-hidden definition.  This would undo the effect of the previous
@@ -930,8 +935,9 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 }
 
 /* Load one audit module.  */
-static void
-load_audit_module (const char *name, struct audit_ifaces **last_audit)
+static void *
+_load_audit_module (const char *name, const void *caller_dlopen,
+                   struct audit_ifaces **last_audit)
 {
   int original_tls_idx = GL(dl_tls_max_dtv_idx);
 
@@ -946,7 +952,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
   if (__glibc_unlikely (err_str != NULL))
     {
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   struct lookup_args largs;
@@ -957,7 +963,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
     {
       unload_audit_module (dlmargs.map, original_tls_idx);
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   unsigned int (*laversion) (unsigned int) = largs.result;
@@ -977,7 +983,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
 file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 			  dlmargs.map->l_name, dlmargs.map->l_ns);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   if (!_dl_audit_check_version (lav))
@@ -986,7 +992,7 @@ file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
 			name, lav, LAV_CURRENT);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   enum { naudit_ifaces = 8 };
@@ -1032,19 +1038,53 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
   /* Now append the new auditing interface to the list.  */
   newp->ifaces.next = NULL;
   if (*last_audit == NULL)
-    *last_audit = GLRO(dl_audit) = &newp->ifaces;
+    *last_audit = GL(dl_audit) = &newp->ifaces;
   else
     *last_audit = (*last_audit)->next = &newp->ifaces;
 
   /* The dynamic linker link map is statically allocated, so the
      cookie in _dl_new_object has not happened.  */
-  link_map_audit_state (&GL (dl_rtld_map), GLRO (dl_naudit))->cookie
+  link_map_audit_state (&GL (dl_rtld_map), GL (dl_naudit))->cookie
     = (intptr_t) &GL (dl_rtld_map);
 
-  ++GLRO(dl_naudit);
+  ++GL(dl_naudit);
 
   /* Mark the DSO as being used for auditing.  */
   dlmargs.map->l_auditing = 1;
+  return dlmargs.map;
+}
+
+static void
+load_audit_module (const char *name, struct audit_ifaces **last_audit)
+{
+  if (_load_audit_module (name, dl_main, last_audit))
+    {
+      struct link_map *l;
+
+      /* Global audit module can audit all existing objects. */
+      for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l; l = l->l_next)
+        l->l_naudit++;
+    }
+}
+
+static void *
+_dlload_audit_module (const char *name, int flags)
+{
+  struct link_map *l;
+  struct audit_ifaces *last_audit = GL(dl_audit);
+
+  /* Find last audit list entry. */
+  while (last_audit && last_audit->next)
+    last_audit = last_audit->next;
+  l = _load_audit_module (name, _dlload_audit_module, &last_audit);
+  if (l)
+    {
+      /* These are not allowed for dynamically-loaded auditors. */
+      last_audit->symbind = NULL;
+      last_audit->ARCH_LA_PLTENTER = NULL;
+      last_audit->ARCH_LA_PLTEXIT = NULL;
+    }
+  return l;
 }
 
 /* Load all audit modules.  */
@@ -1063,7 +1103,7 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
 
   /* Notify audit modules of the initially loaded modules (the main
      program and the dynamic linker itself).  */
-  if (GLRO(dl_naudit) > 0)
+  if (GL(dl_naudit) > 0)
     {
       _dl_audit_objopen (main_map, LM_ID_BASE);
       _dl_audit_objopen (&GL(dl_rtld_map), LM_ID_BASE);
@@ -1822,7 +1862,7 @@ dl_main (const ElfW(Phdr) *phdr,
 
       /* The count based on audit strings may overestimate the number
 	 of audit modules that got loaded, but not underestimate.  */
-      assert (GLRO(dl_naudit) <= naudit);
+      assert (GL(dl_naudit) <= naudit);
     }
 
   /* Keep track of the currently loaded modules to count how many
diff --git a/elf/tst-dynauditmod.c b/elf/tst-dynauditmod.c
new file mode 100644
index 0000000000..322e9ae4c7
--- /dev/null
+++ b/elf/tst-dynauditmod.c
@@ -0,0 +1,190 @@
+/* Audit mod for dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <link.h>
+#include <assert.h>
+
+/* la_objopen() sets the cookie to this value. Other call-backs check
+ * the value to see if la_objopen() was not somehow skipped. */
+#define TST_COOKIE_VAL 12
+
+unsigned int
+la_version (unsigned int version)
+{
+  fprintf (stderr, "%s\n", __func__);
+  return LAV_CURRENT;
+}
+
+char *
+la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+  return (char *) name;
+}
+
+void
+la_activity (uintptr_t *cookie, unsigned int flag)
+{
+  struct link_map *map = (void *) *cookie;
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+}
+
+unsigned int
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+}
+
+unsigned int
+la_objclose (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    {
+      struct link_map *map = (void *) *cookie;
+      fprintf (stderr, "%s\n", map->l_name);
+    }
+  assert (*cookie == TST_COOKIE_VAL);
+  return 0;
+}
+
+void
+la_preinit (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+}
+
+uintptr_t
+#if __ELF_NATIVE_CLASS == 32
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+              uintptr_t *defcook, unsigned int *flags, const char *symname)
+#else
+la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+              uintptr_t *defcook, unsigned int *flags, const char *symname)
+#endif
+{
+  fprintf (stderr, "%s\n", __func__);
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+
+#ifdef __i386__
+Elf32_Addr
+la_i86_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)),
+		     unsigned int ndx __attribute__ ((unused)),
+		     uintptr_t *refcook, uintptr_t *defcook,
+		     La_i86_regs *regs, unsigned int *flags,
+		     const char *symname, long int *framesizep)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+#elif defined __x86_64__
+Elf64_Addr
+la_x86_64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)),
+			unsigned int ndx __attribute__ ((unused)),
+			uintptr_t *refcook, uintptr_t *defcook,
+			La_x86_64_regs *regs, unsigned int *flags,
+			const char *symname, long int *framesizep)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+#elif defined __sparc__ && !defined __arch64__
+Elf32_Addr
+la_sparc32_gnu_pltenter (Elf32_Sym *sym __attribute__ ((unused)),
+			 unsigned int ndx __attribute__ ((unused)),
+			 uintptr_t *refcook, uintptr_t *defcook,
+			 La_sparc32_regs *regs, unsigned int *flags,
+			 const char *symname, long int *framesizep)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+#elif defined __sparc__ && defined __arch64__
+Elf64_Addr
+la_sparc64_gnu_pltenter (Elf64_Sym *sym __attribute__ ((unused)),
+			 unsigned int ndx __attribute__ ((unused)),
+			 uintptr_t *refcook, uintptr_t *defcook,
+			 La_sparc64_regs *regs, unsigned int *flags,
+			 const char *symname, long int *framesizep)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return sym->st_value;
+}
+#elif !defined HAVE_ARCH_PLTENTER
+# warning "pltenter for architecture not supported"
+#endif
+
+#ifdef __i386__
+unsigned int
+la_i86_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+		    uintptr_t *defcook, const struct La_i86_regs *inregs,
+		    struct La_i86_retval *outregs, const char *symname)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return 0;
+}
+#elif defined __x86_64__
+unsigned int
+la_x86_64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+		       uintptr_t *defcook, const struct La_x86_64_regs *inregs,
+		       struct La_x86_64_retval *outregs, const char *symname)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return 0;
+}
+#elif defined __sparc__ && !defined __arch64__
+unsigned int
+la_sparc32_gnu_pltexit (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+			uintptr_t *defcook, const struct La_sparc32_regs *inregs,
+			struct La_sparc32_retval *outregs, const char *symname)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return 0;
+}
+#elif defined __sparc__ && defined __arch64__
+unsigned int
+la_sparc64_gnu_pltexit (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+			uintptr_t *defcook, const struct La_sparc64_regs *inregs,
+			struct La_sparc64_retval *outregs, const char *symname)
+{
+  /* Should not be here in dynamically loaded auditmod. */
+  assert (0);
+  return 0;
+}
+#elif !defined HAVE_ARCH_PLTEXIT
+# warning "pltexit for architecture not supported"
+#endif
diff --git a/elf/tst-loadaudit.c b/elf/tst-loadaudit.c
new file mode 100644
index 0000000000..4bf86d06a8
--- /dev/null
+++ b/elf/tst-loadaudit.c
@@ -0,0 +1,138 @@
+/* Check dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gnu/lib-names.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/support.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+void *
+dlload_audit_module (const char *file, int flags);
+
+static int
+handle_restart (void)
+{
+  void *ah = dlload_audit_module ("tst-dynauditmod.so", 0);
+
+  TEST_VERIFY (ah != NULL);
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
+
+    pid_t (*s) (void) = xdlsym (h, "getpid");
+    TEST_COMPARE (s (), getpid ());
+
+    xdlclose (h);
+  }
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, "tst-audit18mod.so", RTLD_NOW);
+
+    int (*foo) (void) = xdlsym (h, "foo");
+    TEST_COMPARE (foo (), 10);
+
+    xdlclose (h);
+  }
+
+  return 0;
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One our fource parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name  */
+
+  if (restart)
+    return handle_restart ();
+
+  char *spargv[9];
+  int i = 0;
+  for (; i < argc - 1; i++)
+    spargv[i] = argv[i + 1];
+  spargv[i++] = (char *) "--direct";
+  spargv[i++] = (char *) "--restart";
+  spargv[i] = NULL;
+
+  struct support_capture_subprocess result
+    = support_capture_subprogram (spargv[0], spargv);
+  support_capture_subprocess_check (&result, "tst-loadaudit", 0, sc_allow_stderr);
+
+  struct
+  {
+    const char *name;
+    bool found;
+  } audit_iface[] =
+  {
+    { "la_version", false },
+    { "la_objsearch", false },
+    { "la_activity", false },
+    { "la_objopen", false },
+    { "la_objclose", false },
+#if __WORDSIZE == 32
+    { "la_symbind32", false },
+#elif __WORDSIZE == 64
+    { "la_symbind64", false },
+#endif
+#define STRING(s) __STRING (s)
+    { "la_" STRING (ARCH_LA_PLTENTER), false },
+    { "la_" STRING (ARCH_LA_PLTEXIT), false },
+  };
+
+  /* Some hooks are called more than once but the test only check if any
+     is called at least once.  */
+  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
+  TEST_VERIFY (out != NULL);
+  char *buffer = NULL;
+  size_t buffer_length = 0;
+  while (xgetline (&buffer, &buffer_length, out))
+    {
+      for (int i = 0; i < array_length (audit_iface); i++)
+	if (strncmp (buffer, audit_iface[i].name,
+		     strlen (audit_iface[i].name)) == 0)
+	  audit_iface[i].found = true;
+    }
+  free (buffer);
+  xfclose (out);
+
+  /* Check that symbols are resolved, except for the last 3.
+     symbind, pltenter, pltexit are not allowed for dynamically loaded mod. */
+  for (int i = 0; i < array_length (audit_iface); i++)
+    TEST_COMPARE (audit_iface[i].found, i < array_length (audit_iface) - 3);
+
+  support_capture_subprocess_free (&result);
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/include/link.h b/include/link.h
index 1d74feb2bd..31f34540fc 100644
--- a/include/link.h
+++ b/include/link.h
@@ -347,6 +347,7 @@ struct link_map
     size_t l_relro_size;
 
     unsigned long long int l_serial;
+    unsigned int l_naudit;
   };
 
 #include <dl-relocate-ld.h>
diff --git a/manual/dynlink.texi b/manual/dynlink.texi
index 6a4a50d3f0..898354ebe9 100644
--- a/manual/dynlink.texi
+++ b/manual/dynlink.texi
@@ -209,6 +209,7 @@ This function is a GNU extension.
 @c dladdr1
 @c dlclose
 @c dlerror
+@c dlload_audit_module
 @c dlmopen
 @c dlopen
 @c dlsym
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c99dad77cc..1a377061f8 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -399,6 +399,10 @@ struct rtld_global
   /* Used to store the audit information for the link map of the
      dynamic loader.  */
   struct auditstate _dl_rtld_auditstate[DL_NNS];
+
+  /* List of auditing interfaces.  */
+  struct audit_ifaces *_dl_audit;
+  unsigned int _dl_naudit;
 #endif
 
 #if !PTHREAD_IN_LIBC && defined SHARED \
@@ -689,12 +693,11 @@ struct rtld_global_ro
      dlopen.  */
   int (*_dl_find_object) (void *, struct dl_find_object *);
 
+  /* Loads audit module.  */
+  void *(*_dlload_audit_module) (const char *name, int flags);
+
   /* 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;
 };
 # define __rtld_global_attribute__
 # if IS_IN (rtld)
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 4e3200ef55..b3bff7135b 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2294,6 +2294,7 @@ GLIBC_2.36 arc4random_buf F
 GLIBC_2.36 arc4random_uniform F
 GLIBC_2.36 c8rtomb F
 GLIBC_2.36 mbrtoc8 F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index b66fadef40..e0defa9e33 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2633,3 +2633,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index f918bb2d48..fd72907a44 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2730,6 +2730,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 093043a533..b1cd1087bc 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2394,3 +2394,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index f28402fe03..0d124f6f73 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -514,6 +514,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index e2f56880ed..b095d01781 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -511,6 +511,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 319d92356e..25eede829b 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2670,3 +2670,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 6450e17ebe..ea3d616424 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2619,6 +2619,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 0a24ec9afd..a1b6f99e0a 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2803,6 +2803,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 02c65b6482..b8819f22c1 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2568,6 +2568,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index 62faaf4c00..bff3a565ed 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2154,3 +2154,4 @@ GLIBC_2.36 wprintf F
 GLIBC_2.36 write F
 GLIBC_2.36 writev F
 GLIBC_2.36 wscanf F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 16243a7a92..32e766d86e 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -515,6 +515,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 564a553b27..322604f951 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2746,6 +2746,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index e850f47b21..5d2745db86 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2719,3 +2719,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 37178c503f..9607647f7c 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2716,3 +2716,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 3b30b31466..010e0fdb57 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2711,6 +2711,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 0e358570a2..cf178341e9 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2709,6 +2709,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 59c598b98f..2740c1b776 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2717,6 +2717,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 2f7f1ccaf7..92cf1ff6ba 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2619,6 +2619,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 463e01ab84..b712d54d47 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2758,3 +2758,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index ffdb8819d5..38cea3234a 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2140,3 +2140,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 405d40d11c..806a3585ef 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2773,6 +2773,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index ce89602b93..ea51bbafca 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2806,6 +2806,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 849863e639..abbb22d958 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2527,6 +2527,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index b2ccee08c6..3f39ec1ea3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2829,3 +2829,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index ff90d1bff2..42fd36421a 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2396,3 +2396,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index f1017f6ec5..82e85c44c2 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2596,3 +2596,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 5ca051a9eb..4ea4a4202b 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2771,6 +2771,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 0e0b3df973..07bd8e0c36 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2564,6 +2564,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 5b48168ec6..3bb628ccab 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2626,6 +2626,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index c42b39cea8..51efda8b4f 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2623,6 +2623,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 5a0a662dee..e6e6a4f335 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2766,6 +2766,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 9ec4a0bc7f..f8bdf8b036 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2591,6 +2591,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 367c8d0a03..2dda5a2d7d 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2542,6 +2542,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 6a614efb62..23c1f31348 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2648,3 +2648,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127]
  2023-02-23  8:09 [PATCH v2 0/2] minimal run-time audit support Stas Sergeev
@ 2023-02-23  8:09 ` Stas Sergeev
  0 siblings, 0 replies; 8+ messages in thread
From: Stas Sergeev @ 2023-02-23  8:09 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch is an impl of dlload_audit_module() function discussed
in BZ #30127, and the test-case for it, called tst-loadaudit.
It checks the loading of audit module at run-time and makes sure
no "unrecognized" cookie is passed to the module call-backs.

dlload_audit_module (const char *file, int flags)
"file" is a module file name
"flags" are reserved for future use.

As mentioned in BZ #30134, audit module list was in RELRO so this
patch makes it writable. Some hardening measures may be considered
in the future, like disallowing some call-backs (like pltenter)
for dynamically loaded audit modules.

Test-suite run on x86_64 revealed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/Makefile                                |   4 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlaudit.c                               |  62 ++++++++
 elf/Makefile                                  |   6 +
 elf/dl-audit.c                                |  47 +++----
 elf/dl-fini.c                                 |   2 +-
 elf/dl-load.c                                 |   7 +-
 elf/dl-object.c                               |   4 +-
 elf/dl-reloc.c                                |   4 +-
 elf/dl-runtime.c                              |   2 +-
 elf/dl-sym-post.h                             |   2 +-
 elf/do-rel.h                                  |   4 +-
 elf/rtld.c                                    |  55 ++++++--
 elf/tst-dynauditmod.c                         |  92 ++++++++++++
 elf/tst-loadaudit.c                           | 133 ++++++++++++++++++
 include/link.h                                |   1 +
 sysdeps/generic/ldsodefs.h                    |  11 +-
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 52 files changed, 422 insertions(+), 52 deletions(-)
 create mode 100644 dlfcn/dlaudit.c
 create mode 100644 elf/tst-dynauditmod.c
 create mode 100644 elf/tst-loadaudit.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..68f6916d0a 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -44,8 +44,8 @@ install-lib-ldscripts = libdl.so
 $(inst_libdir)/libdl.so:
 
 ifeq ($(build-shared),yes)
-routines += dlopenold
-shared-only-routines := dlopenold
+routines += dlopenold dlaudit
+shared-only-routines := dlopenold dlaudit
 endif
 
 ifeq (yes,$(build-shared))
diff --git a/dlfcn/Versions b/dlfcn/Versions
index cc34eb824d..30145316d1 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -28,6 +28,9 @@ libc {
     dlsym;
     dlvsym;
   }
+  GLIBC_2.38 {
+    dlload_audit_module;
+  }
   GLIBC_PRIVATE {
     __libc_dlerror_result;
     _dlerror_run;
diff --git a/dlfcn/dlaudit.c b/dlfcn/dlaudit.c
new file mode 100644
index 0000000000..b124bbf932
--- /dev/null
+++ b/dlfcn/dlaudit.c
@@ -0,0 +1,62 @@
+/* Load an audit module at run time.
+   Copyright (C) 1995-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <shlib-compat.h>
+
+struct dlload_am_args
+{
+  /* The arguments for dlload_am_doit.  */
+  const char *file;
+  int flags;
+  /* The return value of dlload_am_doit.  */
+  void *new;
+};
+
+static void
+dlload_am_doit (void *a)
+{
+  struct dlload_am_args *args = (struct dlload_am_args *) a;
+
+  if (args->flags)
+    _dl_signal_error (0, NULL, NULL, _("invalid flags parameter"));
+
+  args->new = GLRO(dlload_audit_module) (args->file, args->flags);
+}
+
+static void *
+dlload_am_implementation (const char *file, int flags)
+{
+  struct dlload_am_args args;
+  args.file = file;
+  args.flags = flags;
+
+  return _dlerror_run (dlload_am_doit, &args) ? NULL : args.new;
+}
+
+void *
+___dlload_audit_module (const char *file, int flags)
+{
+  return dlload_am_implementation (file, flags);
+}
+versioned_symbol (libc, ___dlload_audit_module, dlload_audit_module,
+                  GLIBC_2_38);
diff --git a/elf/Makefile b/elf/Makefile
index 2fc6391183..c73103c84a 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -821,6 +821,7 @@ modules-names += \
   tst-deep1mod1 \
   tst-deep1mod2 \
   tst-deep1mod3 \
+  tst-dynauditmod \
   tst-dl_find_object-mod1 \
   tst-dl_find_object-mod2 \
   tst-dl_find_object-mod3 \
@@ -1055,6 +1056,7 @@ ifeq (yes,$(build-shared))
 tests += \
   tst-ifunc-fault-bindnow \
   tst-ifunc-fault-lazy \
+  tst-loadaudit \
   # tests
 # Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
 tests-internal += \
@@ -2349,6 +2351,10 @@ $(objpfx)tst-audit28.out: $(objpfx)tst-auditmod28.so
 $(objpfx)tst-auditmod28.so: $(libsupport)
 tst-audit28-ENV = LD_AUDIT=$(objpfx)tst-auditmod28.so
 
+$(objpfx)tst-loadaudit.out: $(objpfx)tst-dynauditmod.so \
+			  $(objpfx)tst-audit18mod.so
+tst-loadaudit-ARGS = -- $(host-test-program-cmd)
+
 # tst-sonamemove links against an older implementation of the library.
 LDFLAGS-tst-sonamemove-linkmod1.so = \
   -Wl,--version-script=tst-sonamemove-linkmod1.map \
diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index 00e794aa26..0770bd52b2 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -27,8 +27,8 @@
 void
 _dl_audit_activity_map (struct link_map *l, int action)
 {
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->activity != NULL)
 	afct->activity (&link_map_audit_state (l, cnt)->cookie, action);
@@ -43,8 +43,7 @@ _dl_audit_activity_nsid (Lmid_t nsid, int action)
      does not give us a way to signal LA_ACT_CONSISTENT for it because the
      first loaded module is used to identify the namespace.  */
   struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
-      || head == NULL || head->l_auditing)
+  if (__glibc_likely (head == NULL || head->l_naudit == 0 || head->l_auditing))
     return;
 
   _dl_audit_activity_map (head, action);
@@ -56,8 +55,8 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
   if (l == NULL || l->l_auditing || code == 0)
     return name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objsearch != NULL)
 	{
@@ -75,11 +74,11 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
 void
 _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objopen != NULL)
 	{
@@ -95,12 +94,12 @@ _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 void
 _dl_audit_objclose (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0)
+  if (__glibc_likely (l->l_naudit == 0)
       || GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->objclose != NULL)
 	{
@@ -116,11 +115,11 @@ _dl_audit_objclose (struct link_map *l)
 void
 _dl_audit_preinit (struct link_map *l)
 {
-  if (__glibc_likely (GLRO(dl_naudit) == 0))
+  if (__glibc_likely (l->l_naudit == 0))
     return;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->preinit != NULL)
 	afct->preinit (&link_map_audit_state (l, cnt)->cookie);
@@ -145,8 +144,8 @@ _dl_audit_symbind_alt (struct link_map *l, const ElfW(Sym) *ref, void **value,
   ElfW(Sym) sym = *ref;
   sym.st_value = (ElfW(Addr)) *value;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       struct auditstate *match_audit = link_map_audit_state (l, cnt);
       struct auditstate *result_audit = link_map_audit_state (result, cnt);
@@ -212,9 +211,9 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
   const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
 
   unsigned int flags = 0;
-  struct audit_ifaces *afct = GLRO(dl_audit);
+  struct audit_ifaces *afct = GL(dl_audit);
   uintptr_t new_value = (uintptr_t) sym.st_value;
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       /* XXX Check whether both DSOs must request action or only one */
       struct auditstate *l_state = link_map_audit_state (l, cnt);
@@ -267,7 +266,7 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
 		    DL_FIXUP_VALUE_TYPE *value, void *regs, long int *framesize)
 {
   /* Don't do anything if no auditor wants to intercept this call.  */
-  if (GLRO(dl_naudit) == 0
+  if (l->l_naudit == 0
       || (reloc_result->enterexit & LA_SYMB_NOPLTENTER))
     return;
 
@@ -290,8 +289,8 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
   /* Keep track of overwritten addresses.  */
   unsigned int flags = reloc_result->flags;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTENTER != NULL
 	  && (reloc_result->enterexit
@@ -363,8 +362,8 @@ _dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
 					     l_info[DT_STRTAB]);
   const char *symname = strtab + sym.st_name;
 
-  struct audit_ifaces *afct = GLRO(dl_audit);
-  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+  struct audit_ifaces *afct = GL(dl_audit);
+  for (unsigned int cnt = 0; cnt < l->l_naudit; ++cnt)
     {
       if (afct->ARCH_LA_PLTEXIT != NULL
 	  && (reloc_result->enterexit
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 9acb64f47c..a96f769953 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -129,7 +129,7 @@ _dl_fini (void)
     }
 
 #ifdef SHARED
-  if (! do_audit && GLRO(dl_naudit) > 0)
+  if (! do_audit && GL(dl_naudit) > 0)
     {
       do_audit = 1;
       goto again;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fcb39a78d4..720324ccfb 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1032,6 +1032,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
       l->l_addr = l->l_real->l_addr;
       l->l_ld = l->l_real->l_ld;
 
+      /* Do not call audit when this lm closes. */
+      l->l_naudit = 0;
+
       /* No need to bump the refcount of the real object, ld.so will
 	 never be unloaded.  */
       __close_nocancel (fd);
@@ -1607,7 +1610,7 @@ open_verify (const char *name, int fd,
 
 #ifdef SHARED
   /* Give the auditing libraries a chance.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *original_name = name;
       name = _dl_audit_objsearch (name, loader, whatcode);
@@ -2001,7 +2004,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 #ifdef SHARED
   /* Give the auditing libraries a chance to change the name before we
      try anything.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       const char *before = name;
       name = _dl_audit_objsearch (name, loader, LA_SER_ORIG);
diff --git a/elf/dl-object.c b/elf/dl-object.c
index f1f2ec956c..497c57154a 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -77,7 +77,7 @@ _dl_new_object (char *realname, const char *libname, int type,
       naudit = DL_NNS;
     }
   else
-    naudit = GLRO (dl_naudit);
+    naudit = GL (dl_naudit);
 #endif
 
   size_t libname_len = strlen (libname) + 1;
@@ -136,6 +136,8 @@ _dl_new_object (char *realname, const char *libname, int type,
   new->l_ns = nsid;
 
 #ifdef SHARED
+  /* GL(dl_naudit) is a current value and naudit is maximum value. */
+  new->l_naudit = GL (dl_naudit);
   for (unsigned int cnt = 0; cnt < naudit; ++cnt)
     /* No need to initialize bindflags due to calloc.  */
     link_map_audit_state (new, cnt)->cookie = (uintptr_t) new;
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 1d558c1e0c..8aedbc0d96 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -222,8 +222,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
   /* If we are auditing, install the same handlers we need for profiling.  */
   if ((reloc_mode & __RTLD_AUDIT) == 0)
     {
-      struct audit_ifaces *afct = GLRO(dl_audit);
-      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+      struct audit_ifaces *afct = GL(dl_audit);
+      for (unsigned int cnt = 0; cnt < GL(dl_naudit); ++cnt)
 	{
 	  /* Profiling is needed only if PLT hooks are provided.  */
 	  if (afct->ARCH_LA_PLTENTER != NULL
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
index d35a725415..672d6fb702 100644
--- a/elf/dl-runtime.c
+++ b/elf/dl-runtime.c
@@ -313,7 +313,7 @@ _dl_profile_fixup (
       /* Auditing checkpoint: we have a new binding.  Provide the
 	 auditing libraries the possibility to change the value and
 	 tell us whether further auditing is wanted.  */
-      if (defsym != NULL && GLRO(dl_naudit) > 0)
+      if (defsym != NULL && GL(dl_naudit) > 0)
 	_dl_audit_symbind (l, reloc_result, defsym, &value, result);
 #endif
 
diff --git a/elf/dl-sym-post.h b/elf/dl-sym-post.h
index 5623d63ac8..0cc50349ca 100644
--- a/elf/dl-sym-post.h
+++ b/elf/dl-sym-post.h
@@ -50,7 +50,7 @@ _dl_sym_post (lookup_t result, const ElfW(Sym) *ref, void *value,
   /* Auditing checkpoint: we have a new binding.  Provide the
      auditing libraries the possibility to change the value and
      tell us whether further auditing is wanted.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  if (__glibc_unlikely (GL(dl_naudit) > 0))
     {
       if (match == NULL)
         match = _dl_sym_find_caller_link_map (caller);
diff --git a/elf/do-rel.h b/elf/do-rel.h
index 7e1cc4452a..22b5d566eb 100644
--- a/elf/do-rel.h
+++ b/elf/do-rel.h
@@ -148,7 +148,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 #if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym, rversion,
@@ -193,7 +193,7 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
 			       skip_ifunc);
 # if defined SHARED
 	      if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
-		  && GLRO(dl_naudit) > 0)
+		  && GL(dl_naudit) > 0)
 		{
 		  struct link_map *sym_map
 		    = RESOLVE_MAP (map, scope, &sym,
diff --git a/elf/rtld.c b/elf/rtld.c
index f82fbeb132..fbdea6cd3c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -151,6 +151,10 @@ static const char *audit_list_next (struct audit_list *);
 /* Initialize *STATE with the defaults.  */
 static void dl_main_state_init (struct dl_main_state *state);
 
+/* Loads audit module. */
+static void *
+_dlload_audit_module (const char *name, int flags);
+
 /* Process all environments variables the dynamic linker must recognize.
    Since all of them start with `LD_' we are a bit smarter while finding
    all the entries.  */
@@ -272,7 +276,7 @@ audit_list_next (struct audit_list *list)
     }
 }
 
-/* Count audit modules before they are loaded so GLRO(dl_naudit)
+/* Count audit modules before they are loaded so GL(dl_naudit)
    is not yet usable.  */
 static size_t
 audit_list_count (struct audit_list *list)
@@ -375,6 +379,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_error_free = _dl_error_free,
     ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
     ._dl_libc_freeres = __rtld_libc_freeres,
+    ._dlload_audit_module = _dlload_audit_module,
   };
 /* If we would use strong_alias here the compiler would see a
    non-hidden definition.  This would undo the effect of the previous
@@ -930,8 +935,9 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 }
 
 /* Load one audit module.  */
-static void
-load_audit_module (const char *name, struct audit_ifaces **last_audit)
+static void *
+_load_audit_module (const char *name, const void *caller_dlopen,
+                   struct audit_ifaces **last_audit)
 {
   int original_tls_idx = GL(dl_tls_max_dtv_idx);
 
@@ -946,7 +952,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
   if (__glibc_unlikely (err_str != NULL))
     {
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   struct lookup_args largs;
@@ -957,7 +963,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
     {
       unload_audit_module (dlmargs.map, original_tls_idx);
       report_audit_module_load_error (name, err_str, malloced);
-      return;
+      return NULL;
     }
 
   unsigned int (*laversion) (unsigned int) = largs.result;
@@ -977,7 +983,7 @@ load_audit_module (const char *name, struct audit_ifaces **last_audit)
 file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 			  dlmargs.map->l_name, dlmargs.map->l_ns);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   if (!_dl_audit_check_version (lav))
@@ -986,7 +992,7 @@ file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
 ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
 			name, lav, LAV_CURRENT);
       unload_audit_module (dlmargs.map, original_tls_idx);
-      return;
+      return NULL;
     }
 
   enum { naudit_ifaces = 8 };
@@ -1032,19 +1038,44 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
   /* Now append the new auditing interface to the list.  */
   newp->ifaces.next = NULL;
   if (*last_audit == NULL)
-    *last_audit = GLRO(dl_audit) = &newp->ifaces;
+    *last_audit = GL(dl_audit) = &newp->ifaces;
   else
     *last_audit = (*last_audit)->next = &newp->ifaces;
 
   /* The dynamic linker link map is statically allocated, so the
      cookie in _dl_new_object has not happened.  */
-  link_map_audit_state (&GL (dl_rtld_map), GLRO (dl_naudit))->cookie
+  link_map_audit_state (&GL (dl_rtld_map), GL (dl_naudit))->cookie
     = (intptr_t) &GL (dl_rtld_map);
 
-  ++GLRO(dl_naudit);
+  ++GL(dl_naudit);
 
   /* Mark the DSO as being used for auditing.  */
   dlmargs.map->l_auditing = 1;
+  return dlmargs.map;
+}
+
+static void
+load_audit_module (const char *name, struct audit_ifaces **last_audit)
+{
+  struct link_map *l;
+
+  if (_load_audit_module (name, dl_main, last_audit))
+    {
+      /* Global audit module can audit all existing objects. */
+      for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l; l = l->l_next)
+        l->l_naudit++;
+    }
+}
+
+static void *
+_dlload_audit_module (const char *name, int flags)
+{
+  struct audit_ifaces *last_audit = GL(dl_audit);
+
+  /* Find last audit list entry. */
+  while (last_audit && last_audit->next)
+    last_audit = last_audit->next;
+  return _load_audit_module (name, _dlload_audit_module, &last_audit);
 }
 
 /* Load all audit modules.  */
@@ -1063,7 +1094,7 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
 
   /* Notify audit modules of the initially loaded modules (the main
      program and the dynamic linker itself).  */
-  if (GLRO(dl_naudit) > 0)
+  if (GL(dl_naudit) > 0)
     {
       _dl_audit_objopen (main_map, LM_ID_BASE);
       _dl_audit_objopen (&GL(dl_rtld_map), LM_ID_BASE);
@@ -1822,7 +1853,7 @@ dl_main (const ElfW(Phdr) *phdr,
 
       /* The count based on audit strings may overestimate the number
 	 of audit modules that got loaded, but not underestimate.  */
-      assert (GLRO(dl_naudit) <= naudit);
+      assert (GL(dl_naudit) <= naudit);
     }
 
   /* Keep track of the currently loaded modules to count how many
diff --git a/elf/tst-dynauditmod.c b/elf/tst-dynauditmod.c
new file mode 100644
index 0000000000..d132ed1d6f
--- /dev/null
+++ b/elf/tst-dynauditmod.c
@@ -0,0 +1,92 @@
+/* Audit mod for dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <link.h>
+#include <assert.h>
+
+/* la_objopen() sets the cookie to this value. Other call-backs check
+ * the value to see if la_objopen() was not somehow skipped. */
+#define TST_COOKIE_VAL 12
+
+unsigned int
+la_version (unsigned int version)
+{
+  fprintf (stderr, "%s\n", __func__);
+  return LAV_CURRENT;
+}
+
+char *
+la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+  return (char *) name;
+}
+
+void
+la_activity (uintptr_t *cookie, unsigned int flag)
+{
+  struct link_map *map = (void *) *cookie;
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+}
+
+unsigned int
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  fprintf (stderr, "%s\n", map->l_name);
+  *cookie = TST_COOKIE_VAL;
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+}
+
+unsigned int
+la_objclose (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  if (*cookie != TST_COOKIE_VAL)
+    {
+      struct link_map *map = (void *) *cookie;
+      fprintf (stderr, "%s\n", map->l_name);
+    }
+  assert (*cookie == TST_COOKIE_VAL);
+  return 0;
+}
+
+void
+la_preinit (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  assert (*cookie == TST_COOKIE_VAL);
+}
+
+uintptr_t
+#if __ELF_NATIVE_CLASS == 32
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+              uintptr_t *defcook, unsigned int *flags, const char *symname)
+#else
+la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+              uintptr_t *defcook, unsigned int *flags, const char *symname)
+#endif
+{
+  fprintf (stderr, "%s\n", __func__);
+  return sym->st_value;
+}
diff --git a/elf/tst-loadaudit.c b/elf/tst-loadaudit.c
new file mode 100644
index 0000000000..588a89875d
--- /dev/null
+++ b/elf/tst-loadaudit.c
@@ -0,0 +1,133 @@
+/* Check dlload_audit_module.
+   Copyright (C) 2021-2023 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, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gnu/lib-names.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/support.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+void *
+dlload_audit_module (const char *file, int flags);
+
+static int
+handle_restart (void)
+{
+  void *ah = dlload_audit_module ("tst-dynauditmod.so", 0);
+
+  TEST_VERIFY (ah != NULL);
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
+
+    pid_t (*s) (void) = xdlsym (h, "getpid");
+    TEST_COMPARE (s (), getpid ());
+
+    xdlclose (h);
+  }
+
+  {
+    void *h = xdlmopen (LM_ID_NEWLM, "tst-audit18mod.so", RTLD_NOW);
+
+    int (*foo) (void) = xdlsym (h, "foo");
+    TEST_COMPARE (foo (), 10);
+
+    xdlclose (h);
+  }
+
+  return 0;
+}
+
+static int
+do_test (int argc, char *argv[])
+{
+  /* We must have either:
+     - One our fource parameters left if called initially:
+       + path to ld.so         optional
+       + "--library-path"      optional
+       + the library path      optional
+       + the application name  */
+
+  if (restart)
+    return handle_restart ();
+
+  char *spargv[9];
+  int i = 0;
+  for (; i < argc - 1; i++)
+    spargv[i] = argv[i + 1];
+  spargv[i++] = (char *) "--direct";
+  spargv[i++] = (char *) "--restart";
+  spargv[i] = NULL;
+
+  struct support_capture_subprocess result
+    = support_capture_subprogram (spargv[0], spargv);
+  support_capture_subprocess_check (&result, "tst-loadaudit", 0, sc_allow_stderr);
+
+  struct
+  {
+    const char *name;
+    bool found;
+  } audit_iface[] =
+  {
+    { "la_version", false },
+    { "la_objsearch", false },
+    { "la_activity", false },
+    { "la_objopen", false },
+    { "la_objclose", false },
+#if __WORDSIZE == 32
+    { "la_symbind32", false },
+#elif __WORDSIZE == 64
+    { "la_symbind64", false },
+#endif
+  };
+
+  /* Some hooks are called more than once but the test only check if any
+     is called at least once.  */
+  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
+  TEST_VERIFY (out != NULL);
+  char *buffer = NULL;
+  size_t buffer_length = 0;
+  while (xgetline (&buffer, &buffer_length, out))
+    {
+      for (int i = 0; i < array_length (audit_iface); i++)
+	if (strncmp (buffer, audit_iface[i].name,
+		     strlen (audit_iface[i].name)) == 0)
+	  audit_iface[i].found = true;
+    }
+  free (buffer);
+  xfclose (out);
+
+  for (int i = 0; i < array_length (audit_iface); i++)
+    TEST_COMPARE (audit_iface[i].found, true);
+
+  support_capture_subprocess_free (&result);
+
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/include/link.h b/include/link.h
index 1d74feb2bd..31f34540fc 100644
--- a/include/link.h
+++ b/include/link.h
@@ -347,6 +347,7 @@ struct link_map
     size_t l_relro_size;
 
     unsigned long long int l_serial;
+    unsigned int l_naudit;
   };
 
 #include <dl-relocate-ld.h>
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c99dad77cc..1a377061f8 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -399,6 +399,10 @@ struct rtld_global
   /* Used to store the audit information for the link map of the
      dynamic loader.  */
   struct auditstate _dl_rtld_auditstate[DL_NNS];
+
+  /* List of auditing interfaces.  */
+  struct audit_ifaces *_dl_audit;
+  unsigned int _dl_naudit;
 #endif
 
 #if !PTHREAD_IN_LIBC && defined SHARED \
@@ -689,12 +693,11 @@ struct rtld_global_ro
      dlopen.  */
   int (*_dl_find_object) (void *, struct dl_find_object *);
 
+  /* Loads audit module.  */
+  void *(*_dlload_audit_module) (const char *name, int flags);
+
   /* 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;
 };
 # define __rtld_global_attribute__
 # if IS_IN (rtld)
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 4e3200ef55..b3bff7135b 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2294,6 +2294,7 @@ GLIBC_2.36 arc4random_buf F
 GLIBC_2.36 arc4random_uniform F
 GLIBC_2.36 c8rtomb F
 GLIBC_2.36 mbrtoc8 F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index b66fadef40..e0defa9e33 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2633,3 +2633,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index f918bb2d48..fd72907a44 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2730,6 +2730,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index 093043a533..b1cd1087bc 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2394,3 +2394,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index f28402fe03..0d124f6f73 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -514,6 +514,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index e2f56880ed..b095d01781 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -511,6 +511,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 319d92356e..25eede829b 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2670,3 +2670,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 6450e17ebe..ea3d616424 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2619,6 +2619,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 0a24ec9afd..a1b6f99e0a 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2803,6 +2803,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 02c65b6482..b8819f22c1 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2568,6 +2568,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index 62faaf4c00..bff3a565ed 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2154,3 +2154,4 @@ GLIBC_2.36 wprintf F
 GLIBC_2.36 write F
 GLIBC_2.36 writev F
 GLIBC_2.36 wscanf F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 16243a7a92..32e766d86e 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -515,6 +515,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 564a553b27..322604f951 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2746,6 +2746,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index e850f47b21..5d2745db86 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2719,3 +2719,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 37178c503f..9607647f7c 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2716,3 +2716,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 3b30b31466..010e0fdb57 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2711,6 +2711,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 0e358570a2..cf178341e9 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2709,6 +2709,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 59c598b98f..2740c1b776 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2717,6 +2717,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 2f7f1ccaf7..92cf1ff6ba 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2619,6 +2619,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 463e01ab84..b712d54d47 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2758,3 +2758,4 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index ffdb8819d5..38cea3234a 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2140,3 +2140,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 405d40d11c..806a3585ef 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2773,6 +2773,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index ce89602b93..ea51bbafca 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2806,6 +2806,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 849863e639..abbb22d958 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2527,6 +2527,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index b2ccee08c6..3f39ec1ea3 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2829,3 +2829,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index ff90d1bff2..42fd36421a 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2396,3 +2396,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index f1017f6ec5..82e85c44c2 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2596,3 +2596,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 5ca051a9eb..4ea4a4202b 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2771,6 +2771,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 0e0b3df973..07bd8e0c36 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2564,6 +2564,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index 5b48168ec6..3bb628ccab 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2626,6 +2626,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index c42b39cea8..51efda8b4f 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2623,6 +2623,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 5a0a662dee..e6e6a4f335 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2766,6 +2766,7 @@ GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
 GLIBC_2.37 __ppoll64_chk F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 9ec4a0bc7f..f8bdf8b036 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2591,6 +2591,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 367c8d0a03..2dda5a2d7d 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2542,6 +2542,7 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 6a614efb62..23c1f31348 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2648,3 +2648,4 @@ GLIBC_2.36 pidfd_open F
 GLIBC_2.36 pidfd_send_signal F
 GLIBC_2.36 process_madvise F
 GLIBC_2.36 process_mrelease F
+GLIBC_2.38 dlload_audit_module F
-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2023-03-01 15:08 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-21 15:33 [PATCH 0/2] minimal run-time audit support Stas Sergeev
2023-02-21 15:33 ` [PATCH 1/2] elf/dl-open: fix audit wrt RTLD_NOLOAD [BZ #30127] Stas Sergeev
2023-02-21 15:33 ` [PATCH 2/2] dlfcn,elf: impl dlload_audit_module " Stas Sergeev
2023-02-23  8:09 [PATCH v2 0/2] minimal run-time audit support Stas Sergeev
2023-02-23  8:09 ` [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127] Stas Sergeev
2023-02-24 16:53 [PATCH v3 0/2] minimal run-time audit support Stas Sergeev
2023-02-24 16:54 ` [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127] Stas Sergeev
2023-02-26 11:16 [PATCH v4 0/2] minimal run-time audit support Stas Sergeev
2023-02-26 11:16 ` [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127] Stas Sergeev
2023-02-28 16:51 [PATCH v5 0/2] minimal run-time audit support Stas Sergeev
2023-02-28 16:51 ` [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127] Stas Sergeev
2023-03-01 15:08 [PATCH v6 0/2] minimal run-time audit support Stas Sergeev
2023-03-01 15:08 ` [PATCH 2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127] Stas Sergeev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).