[2/2] dlfcn,elf: impl dlload_audit_module [BZ #30127]

Message ID 20230226111637.1541915-3-stsp2@yandex.ru
State Superseded
Headers
Series minimal run-time audit support |

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent
dj/TryBot-32bit success Build for i686

Commit Message

stsp Feb. 26, 2023, 11:16 a.m. UTC
  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
  

Patch

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