public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v7 0/3] implement dlmem() with audit extensions
@ 2023-02-15 16:55 Stas Sergeev
  2023-02-15 16:55 ` [PATCH 1/3] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Stas Sergeev @ 2023-02-15 16:55 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

Changes in v7:
- add _dl_audit_premap audit extension and its usage example

Changes in v6:
- use __strdup("") for l_name as suggested by Andreas Schwab

Changes in v5:
- added _dl_audit_premap_dlmem audit extension for dlmem
- added tst-auditmod-dlmem.c test-case that feeds shm fd to dlmem()

Changes in v4:
- re-target to GLIBC_2.38
- add tst-auditdlmem.c test-case to test auditing
- drop length page-aligning in tst-dlmem: mmap() aligns length on its own
- bugfix: in do_mmapcpy() allow mmaps past end of buffer

Changes in v3:
- Changed prototype of dlmem() (and all the internal machinery) to
  use "const unsigned char *buffer" instead of "const char *buffer".

Changes in v2:
- use <support/test-driver.c> instead of "../test-skeleton.c"
- re-target to GLIBC_2.37
- update all libc.abilist files


Patch 1: fix memory management bug in _dl_new_object()
Patch 2: implement dlmem() and its audit extension
Patch 3: add _dl_audit_premap audit extension and usage example

-- 
2.37.2


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

* [PATCH 1/3] elf: strdup() l_name if no realname [BZ #30100]
  2023-02-15 16:55 [PATCH v7 0/3] implement dlmem() with audit extensions Stas Sergeev
@ 2023-02-15 16:55 ` Stas Sergeev
  2023-02-15 16:55 ` [PATCH 2/3] dlfcn,elf: implement dlmem() and audit [BZ #11767] Stas Sergeev
  2023-02-15 16:55 ` [PATCH 3/3] elf/dl-audit: add _dl_audit_premap fn [BZ #30007] Stas Sergeev
  2 siblings, 0 replies; 7+ messages in thread
From: Stas Sergeev @ 2023-02-15 16:55 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

_dl_close_worker() has this code:
      /* This name always is allocated.  */
      free (imap->l_name);

But in that particular case, while indeed being allocated, l_name
doesn't point to the start of an allocation:
  new = (struct link_map *) calloc (sizeof (*new) + audit_space
                                    + sizeof (struct link_map *)
                                    + sizeof (*newname) + libname_len, 1);
  ...
  new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1)
                                                            + audit_space);

  new->l_libname = newname
    = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1);
  newname->name = (char *) memcpy (newname + 1, libname, libname_len);
  ...
  new->l_name = (char *) newname->name + libname_len - 1;

It therefore cannot be freed separately.
Use strdup("") as a simple fix.

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

diff --git a/elf/dl-object.c b/elf/dl-object.c
index f1f2ec956c..ab926cd4bf 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -122,7 +122,10 @@ _dl_new_object (char *realname, const char *libname, int type,
 #endif
     new->l_name = realname;
   else
-    new->l_name = (char *) newname->name + libname_len - 1;
+    /* When realname="", it is not allocated and points to the constant
+       string. Constness is dropped by an explicit cast. :(
+       So strdup() it here. */
+    new->l_name = __strdup ("");
 
   new->l_type = type;
   /* If we set the bit now since we know it is never used we avoid
-- 
2.37.2


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

* [PATCH 2/3] dlfcn,elf: implement dlmem() and audit [BZ #11767]
  2023-02-15 16:55 [PATCH v7 0/3] implement dlmem() with audit extensions Stas Sergeev
  2023-02-15 16:55 ` [PATCH 1/3] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
@ 2023-02-15 16:55 ` Stas Sergeev
  2023-02-20 15:50   ` Carlos O'Donell
  2023-02-15 16:55 ` [PATCH 3/3] elf/dl-audit: add _dl_audit_premap fn [BZ #30007] Stas Sergeev
  2 siblings, 1 reply; 7+ messages in thread
From: Stas Sergeev @ 2023-02-15 16:55 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch adds the following function:
void *dlmem(const unsigned char *buffer, size_t size, int flags);

It is the same as dlopen() but allows to dynamic-link solibs from
the memory buffer, rather than from a file as dlopen() does.

"buffer" arg is the pointer to the solib image in memory.
"size" is the solib image size. Must be smaller-or-equal to the
actual buffer size.
"flags" is the same flags argument used in dlopen().

The idea behind the implementation is very simple: where the
dlopen() would mmap() the file, dlmem() does anonymous
mmap()+memcpy().
Unfortunately the glibc code was too bound to the file reads,
so the patch looks bigger than it should. Some refactorings
were needed to avoid big copy/pasts and code duplications.
In particular, _dl_map_object_from_fd() was split and now calls
2 functions that are also called from __dl_map_object_from_mem().
The same treatment was applied to open_verify() - the part that
can be shared, was split into do_open_verify().

This patch adds a test-case named tst-dlmem.

This patch also adds the _dl_audit_premap_dlmem LD_AUDIT
extension. It passes map length to la_premap_dlmem() and gets
back an fd. If returned fd==-1 then the dlmem() space is
anonymously mapped, which is a default behavior w/o audit.
But it is possible to create a shm area of the needed size
with shm_open()/ftruncate() and return that fd. Then you
will be able to access the solib image via that fd, so that
you can mmap() it to another process or another address of
the same process.

The test-case for that functionality is called tst-audit-dlmem.

The test-suite was run on x86_64/64 and showed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/Makefile                                |    5 +-
 dlfcn/Versions                                |    3 +
 dlfcn/dlmem.c                                 |  105 ++
 dlfcn/tst-dlmem.c                             |  100 ++
 elf/Makefile                                  |    7 +
 elf/dl-audit.c                                |   22 +
 elf/dl-load.c                                 | 1023 ++++++++++-------
 elf/dl-load.h                                 |    8 +-
 elf/dl-main.h                                 |   21 +
 elf/dl-map-segments.h                         |   31 +-
 elf/dl-open.c                                 |   37 +-
 elf/link.h                                    |    1 +
 elf/rtld.c                                    |    4 +-
 elf/tst-audit-dlmem.c                         |  229 ++++
 elf/tst-audit18mod.c                          |    2 +
 elf/tst-auditmod-dlmem.c                      |  102 ++
 include/dlfcn.h                               |    4 +
 manual/dynlink.texi                           |    1 +
 sysdeps/generic/ldsodefs.h                    |   13 +
 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 +
 54 files changed, 1339 insertions(+), 414 deletions(-)
 create mode 100644 dlfcn/dlmem.c
 create mode 100644 dlfcn/tst-dlmem.c
 create mode 100644 elf/tst-audit-dlmem.c
 create mode 100644 elf/tst-auditmod-dlmem.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..c6deef6e43 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -28,6 +28,7 @@ routines = \
   dlclose \
   dlerror \
   dlinfo \
+  dlmem \
   dlmopen \
   dlopen \
   dlsym \
@@ -51,7 +52,8 @@ endif
 ifeq (yes,$(build-shared))
 tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
 	bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
-	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen
+	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem
+CPPFLAGS-tst-dlmem.c += -DBUILDDIR=\"$(objpfx)\"
 endif
 modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
 		defaultmod2 errmsg1mod modatexit modcxaatexit \
@@ -102,6 +104,7 @@ $(objpfx)glrefmain.out: $(objpfx)glrefmain \
 $(objpfx)failtest.out: $(objpfx)failtestmod.so
 
 $(objpfx)tst-dladdr.out: $(objpfx)glreflib1.so
+$(objpfx)tst-dlmem.out: $(objpfx)glreflib1.so
 
 $(objpfx)tst-dlinfo.out: $(objpfx)glreflib3.so
 LDFLAGS-glreflib3.so = -Wl,-rpath,:
diff --git a/dlfcn/Versions b/dlfcn/Versions
index cc34eb824d..b427c9c3a3 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -28,6 +28,9 @@ libc {
     dlsym;
     dlvsym;
   }
+  GLIBC_2.38 {
+    dlmem;
+  }
   GLIBC_PRIVATE {
     __libc_dlerror_result;
     _dlerror_run;
diff --git a/dlfcn/dlmem.c b/dlfcn/dlmem.c
new file mode 100644
index 0000000000..b8efd161ad
--- /dev/null
+++ b/dlfcn/dlmem.c
@@ -0,0 +1,105 @@
+/* Load a shared object from memory.
+   Copyright (C) 1995-2022 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 dlmem_args
+{
+  /* The arguments for dlmem_doit.  */
+  const unsigned char *buffer;
+  size_t size;
+  int mode;
+  /* The return value of dlmem_doit.  */
+  void *new;
+  /* Address of the caller.  */
+  const void *caller;
+};
+
+
+/* Non-shared code has no support for multiple namespaces.  */
+#ifdef SHARED
+# define NS __LM_ID_CALLER
+#else
+# define NS LM_ID_BASE
+#endif
+
+
+static void
+dlmem_doit (void *a)
+{
+  struct dlmem_args *args = (struct dlmem_args *) a;
+
+  if (args->mode & ~(RTLD_BINDING_MASK | RTLD_NOLOAD | RTLD_DEEPBIND
+		     | RTLD_GLOBAL | RTLD_LOCAL | RTLD_NODELETE
+		     | __RTLD_SPROF))
+    _dl_signal_error (0, NULL, NULL, _("invalid mode parameter"));
+
+  args->new = GLRO(dl_mem) (args->buffer, args->size,
+			    args->mode | __RTLD_DLOPEN,
+			    args->caller,
+			    NS,
+			    __libc_argc, __libc_argv, __environ);
+}
+
+
+static void *
+dlmem_implementation (const unsigned char *buffer, size_t size, int mode,
+                      void *dl_caller)
+{
+  struct dlmem_args args;
+  args.buffer = buffer;
+  args.size = size;
+  args.mode = mode;
+  args.caller = dl_caller;
+
+  return _dlerror_run (dlmem_doit, &args) ? NULL : args.new;
+}
+
+#ifdef SHARED
+void *
+___dlmem (const unsigned char *buffer, size_t size, int mode)
+{
+  if (GLRO (dl_dlfcn_hook) != NULL)
+    return GLRO (dl_dlfcn_hook)->dlmem (buffer, size, mode,
+                                        RETURN_ADDRESS (0));
+  else
+    return dlmem_implementation (buffer, size, mode, RETURN_ADDRESS (0));
+}
+versioned_symbol (libc, ___dlmem, dlmem, GLIBC_2_38);
+
+#else /* !SHARED */
+/* Also used with _dlfcn_hook.  */
+void *
+__dlmem (const unsigned char *buffer, size_t size, int mode, void *dl_caller)
+{
+  return dlmem_implementation (buffer, size, mode, dl_caller);
+}
+
+void *
+___dlmem (const unsigned char *buffer, size_t size, int mode)
+{
+  return __dlmem (buffer, size, mode, RETURN_ADDRESS (0));
+}
+weak_alias (___dlmem, dlmem)
+static_link_warning (dlmem)
+#endif /* !SHARED */
diff --git a/dlfcn/tst-dlmem.c b/dlfcn/tst-dlmem.c
new file mode 100644
index 0000000000..da9865cc43
--- /dev/null
+++ b/dlfcn/tst-dlmem.c
@@ -0,0 +1,100 @@
+/* Test for dlmem.
+   Copyright (C) 2000-2022 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 <link.h>
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+
+#define TEST_FUNCTION do_test
+extern int do_test (void);
+void *dlmem (const unsigned char *buffer, size_t size, int flags);
+
+int
+do_test (void)
+{
+  void *handle;
+  void *addr;
+  int (*sym) (void); /* We load ref1 from glreflib1.c.  */
+  Dl_info info;
+  int ret;
+  int fd;
+  int num;
+  off_t len;
+  struct link_map *lm;
+
+  fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
+  if (fd == -1)
+    error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
+  len = lseek (fd, 0, SEEK_END);
+  lseek (fd, 0, SEEK_SET);
+  addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (addr == MAP_FAILED)
+    error (EXIT_FAILURE, 0, "cannot mmap: glreflib1.so");
+  handle = dlmem (addr, len, RTLD_NOW);
+  if (handle == NULL)
+    error (EXIT_FAILURE, 0, "cannot load: glreflib1.so");
+  munmap (addr, len);
+  close (fd);
+
+  sym = dlsym (handle, "ref1");
+  if (sym == NULL)
+    error (EXIT_FAILURE, 0, "dlsym failed");
+
+  memset (&info, 0, sizeof (info));
+  ret = dladdr (sym, &info);
+  if (ret == 0)
+    error (EXIT_FAILURE, 0, "dladdr failed");
+  ret = dlinfo (handle, RTLD_DI_LINKMAP, &lm);
+  if (ret != 0)
+    error (EXIT_FAILURE, 0, "dlinfo failed");
+
+  printf ("ret = %d\n", ret);
+  printf ("info.dli_fname = %p (\"%s\")\n", info.dli_fname, info.dli_fname);
+  printf ("info.dli_fbase = %p\n", info.dli_fbase);
+  printf ("info.dli_sname = %p (\"%s\")\n", info.dli_sname, info.dli_sname);
+  printf ("info.dli_saddr = %p\n", info.dli_saddr);
+  printf ("lm->l_addr = %lx\n", lm->l_addr);
+
+  num = sym ();
+  if (num != 42)
+    error (EXIT_FAILURE, 0, "bad return from ref1");
+
+  if (info.dli_fname == NULL)
+    error (EXIT_FAILURE, 0, "dli_fname is NULL");
+  if (info.dli_fbase == NULL)
+    error (EXIT_FAILURE, 0, "dli_fbase is NULL");
+  if (info.dli_sname == NULL)
+    error (EXIT_FAILURE, 0, "dli_sname is NULL");
+  if (info.dli_saddr == NULL)
+    error (EXIT_FAILURE, 0, "dli_saddr is NULL");
+
+  dlclose (handle);
+
+  return 0;
+}
+
+
+#include <support/test-driver.c>
diff --git a/elf/Makefile b/elf/Makefile
index 2fc6391183..5b3e74f2eb 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -369,6 +369,7 @@ tests += \
   tst-align \
   tst-align2 \
   tst-align3 \
+  tst-audit-dlmem \
   tst-audit-tlsdesc \
   tst-audit-tlsdesc-dlopen \
   tst-audit1 \
@@ -796,6 +797,7 @@ modules-names += \
   tst-auditmanymod7 \
   tst-auditmanymod8 \
   tst-auditmanymod9 \
+  tst-auditmod-dlmem  \
   tst-auditmod-tlsdesc  \
   tst-auditmod1 \
   tst-auditmod9a \
@@ -2259,6 +2261,11 @@ $(objpfx)tst-audit18.out: $(objpfx)tst-auditmod18.so \
 			  $(objpfx)tst-audit18mod.so
 tst-audit18-ARGS = -- $(host-test-program-cmd)
 
+$(objpfx)tst-audit-dlmem.out: $(objpfx)tst-auditmod-dlmem.so \
+			  $(objpfx)tst-audit18mod.so
+tst-audit-dlmem-ARGS = -- $(host-test-program-cmd)
+CPPFLAGS-tst-audit-dlmem.c += -DBUILDDIR=\"$(objpfx)\"
+
 $(objpfx)tst-audit19a.out: $(objpfx)tst-auditmod19a.so
 tst-audit19a-ENV = LD_AUDIT=$(objpfx)tst-auditmod19a.so
 
diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index 00e794aa26..76bf365168 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -72,6 +72,28 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code)
   return name;
 }
 
+int
+_dl_audit_premap_dlmem (struct link_map *l, size_t maplength)
+{
+  if (__glibc_likely (GLRO(dl_naudit) == 0))
+    return -1;
+
+  struct audit_ifaces *afct = GLRO(dl_audit);
+  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+    {
+      if (afct->premap_dlmem != NULL)
+	{
+	  struct auditstate *state = link_map_audit_state (l, cnt);
+	  int fd = afct->premap_dlmem (maplength, &state->cookie);
+	  if (fd != -1)
+	    return fd;
+	}
+
+      afct = afct->next;
+   }
+   return -1;
+}
+
 void
 _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 {
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fcb39a78d4..7baf70c0de 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -55,7 +55,8 @@ struct filebuf
 #else
 # define FILEBUF_SIZE 832
 #endif
-  char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
+  ssize_t allocated;
+  char *buf;
 };
 
 #include "dynamic-link.h"
@@ -74,6 +75,7 @@ struct filebuf
 #include <dl-machine-reject-phdr.h>
 #include <dl-sysdep-open.h>
 #include <dl-prop.h>
+#include <dl-main.h>
 #include <not-cancel.h>
 
 #include <endian.h>
@@ -124,6 +126,29 @@ static const size_t system_dirs_len[] =
 };
 #define nsystem_dirs_len array_length (system_dirs_len)
 
+static void
+filebuf_done (struct filebuf *fb)
+{
+  free (fb->buf);
+  fb->buf = NULL;
+  fb->allocated = 0;
+}
+
+static bool
+filebuf_ensure (struct filebuf *fb, size_t size)
+{
+  bool ret = false;
+
+  if (size > fb->allocated)
+    {
+      size_t new_len = size + FILEBUF_SIZE;
+      fb->buf = realloc (fb->buf, new_len);
+      fb->allocated = new_len;
+      ret = true;
+    }
+  return ret;
+}
+
 static bool
 is_trusted_path_normalize (const char *path, size_t len)
 {
@@ -929,147 +954,30 @@ _dl_process_pt_gnu_property (struct link_map *l, int fd, const ElfW(Phdr) *ph)
     }
 }
 
+typedef void _dl_premap_type (struct link_map *l, void *private,
+                              size_t maplength);
 
-/* Map in the shared object NAME, actually located in REALNAME, and already
-   opened on FD.  */
-
-#ifndef EXTERNAL_MAP_FROM_FD
-static
-#endif
-struct link_map *
-_dl_map_object_from_fd (const char *name, const char *origname, int fd,
-			struct filebuf *fbp, char *realname,
-			struct link_map *loader, int l_type, int mode,
-			void **stack_endp, Lmid_t nsid)
+static int
+_ld_map_object_1 (struct link_map *l, void *fd,
+                  struct filebuf *fbp,
+                  int mode, struct link_map *loader,
+                  void **stack_endp, int *errval_p,
+                  const char **errstring_p,
+                  __typeof (do_mmap) *m_map,
+                  _dl_premap_type *premap)
 {
-  struct link_map *l = NULL;
   const ElfW(Ehdr) *header;
   const ElfW(Phdr) *phdr;
   const ElfW(Phdr) *ph;
   size_t maplength;
   int type;
   /* Initialize to keep the compiler happy.  */
-  const char *errstring = NULL;
-  int errval = 0;
-  struct r_debug *r = _dl_debug_update (nsid);
-  bool make_consistent = false;
-
-  /* Get file information.  To match the kernel behavior, do not fill
-     in this information for the executable in case of an explicit
-     loader invocation.  */
-  struct r_file_id id;
-  if (mode & __RTLD_OPENEXEC)
-    {
-      assert (nsid == LM_ID_BASE);
-      memset (&id, 0, sizeof (id));
-    }
-  else
-    {
-      if (__glibc_unlikely (!_dl_get_file_id (fd, &id)))
-	{
-	  errstring = N_("cannot stat shared object");
-	lose_errno:
-	  errval = errno;
-	lose:
-	  /* The file might already be closed.  */
-	  if (fd != -1)
-	    __close_nocancel (fd);
-	  if (l != NULL && l->l_map_start != 0)
-	    _dl_unmap_segments (l);
-	  if (l != NULL && l->l_origin != (char *) -1l)
-	    free ((char *) l->l_origin);
-	  if (l != NULL && !l->l_libname->dont_free)
-	    free (l->l_libname);
-	  if (l != NULL && l->l_phdr_allocated)
-	    free ((void *) l->l_phdr);
-	  free (l);
-	  free (realname);
-
-	  if (make_consistent && r != NULL)
-	    {
-	      r->r_state = RT_CONSISTENT;
-	      _dl_debug_state ();
-	      LIBC_PROBE (map_failed, 2, nsid, r);
-	    }
-
-	  _dl_signal_error (errval, name, NULL, errstring);
-	}
-
-      /* Look again to see if the real name matched another already loaded.  */
-      for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
-	if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
-	  {
-	    /* The object is already loaded.
-	       Just bump its reference count and return it.  */
-	    __close_nocancel (fd);
-
-	    /* If the name is not in the list of names for this object add
-	       it.  */
-	    free (realname);
-	    add_name_to_object (l, name);
-
-	    return l;
-	  }
-    }
-
-#ifdef SHARED
-  /* When loading into a namespace other than the base one we must
-     avoid loading ld.so since there can only be one copy.  Ever.  */
-  if (__glibc_unlikely (nsid != LM_ID_BASE)
-      && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id)
-	  || _dl_name_match_p (name, &GL(dl_rtld_map))))
-    {
-      /* This is indeed ld.so.  Create a new link_map which refers to
-	 the real one for almost everything.  */
-      l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
-      if (l == NULL)
-	goto fail_new;
-
-      /* Refer to the real descriptor.  */
-      l->l_real = &GL(dl_rtld_map);
-
-      /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen().  */
-      l->l_addr = l->l_real->l_addr;
-      l->l_ld = l->l_real->l_ld;
-
-      /* No need to bump the refcount of the real object, ld.so will
-	 never be unloaded.  */
-      __close_nocancel (fd);
-
-      /* Add the map for the mirrored object to the object list.  */
-      _dl_add_to_namespace_list (l, nsid);
-
-      return l;
-    }
-#endif
-
-  if (mode & RTLD_NOLOAD)
-    {
-      /* We are not supposed to load the object unless it is already
-	 loaded.  So return now.  */
-      free (realname);
-      __close_nocancel (fd);
-      return NULL;
-    }
-
-  /* Print debugging message.  */
-  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
-    _dl_debug_printf ("file=%s [%lu];  generating link map\n", name, nsid);
+#define errstring (*errstring_p)
+#define errval (*errval_p)
 
   /* This is the ELF header.  We read it in `open_verify'.  */
   header = (void *) fbp->buf;
 
-  /* Enter the new object in the list of loaded objects.  */
-  l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
-  if (__glibc_unlikely (l == NULL))
-    {
-#ifdef SHARED
-    fail_new:
-#endif
-      errstring = N_("cannot create shared object descriptor");
-      goto lose_errno;
-    }
-
   /* Extract the remaining details we need from the ELF header
      and then read in the program header table.  */
   l->l_entry = header->e_entry;
@@ -1077,23 +985,13 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   l->l_phnum = header->e_phnum;
 
   maplength = header->e_phnum * sizeof (ElfW(Phdr));
-  if (header->e_phoff + maplength <= (size_t) fbp->len)
-    phdr = (void *) (fbp->buf + header->e_phoff);
-  else
-    {
-      phdr = alloca (maplength);
-      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
-				       header->e_phoff) != maplength)
-	{
-	  errstring = N_("cannot read file data");
-	  goto lose_errno;
-	}
-    }
+  assert (header->e_phoff + maplength <= (size_t) fbp->len);
+  phdr = (void *) (fbp->buf + header->e_phoff);
 
    /* On most platforms presume that PT_GNU_STACK is absent and the stack is
     * executable.  Other platforms default to a nonexecutable stack and don't
     * need PT_GNU_STACK to do so.  */
-   unsigned int stack_flags = DEFAULT_STACK_PERMS;
+  unsigned int stack_flags = DEFAULT_STACK_PERMS;
 
   {
     /* Scan the program header table, collecting its load commands.  */
@@ -1261,12 +1159,15 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
     /* Length of the sections to be loaded.  */
     maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart;
 
+    if (premap)
+      premap(l, fd, maplength);
+
     /* Now process the load commands and map segments into memory.
        This is responsible for filling in:
        l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr
      */
     errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds,
-				  maplength, has_holes, loader);
+				  maplength, has_holes, loader, m_map);
     if (__glibc_unlikely (errstring != NULL))
       {
 	/* Mappings can be in an inconsistent state: avoid unmap.  */
@@ -1379,22 +1280,13 @@ cannot enable executable stack as shared object requires");
     switch (ph[-1].p_type)
       {
       case PT_NOTE:
-	_dl_process_pt_note (l, fd, &ph[-1]);
+	_dl_process_pt_note (l, -1, &ph[-1]);
 	break;
       case PT_GNU_PROPERTY:
-	_dl_process_pt_gnu_property (l, fd, &ph[-1]);
+	_dl_process_pt_gnu_property (l, -1, &ph[-1]);
 	break;
       }
 
-  /* We are done mapping in the file.  We no longer need the descriptor.  */
-  if (__glibc_unlikely (__close_nocancel (fd) != 0))
-    {
-      errstring = N_("cannot close file descriptor");
-      goto lose_errno;
-    }
-  /* Signal that we closed the file.  */
-  fd = -1;
-
   /* Failures before this point are handled locally via lose.
      There are no more failures in this function until return,
      to change that the cleanup handling needs to be updated.  */
@@ -1419,6 +1311,23 @@ cannot enable executable stack as shared object requires");
 			   (unsigned long int) l->l_phdr,
 			   (int) sizeof (void *) * 2, l->l_phnum);
 
+  return 0;
+
+lose_errno:
+  errval = errno;
+lose:
+  return -1;
+
+#undef errval
+#undef errstring
+}
+
+static void
+_ld_map_object_2 (struct link_map *l, int mode,
+                  struct r_file_id id, const char *origname,
+                  Lmid_t nsid, struct r_debug *r,
+                  bool *make_consistent_p)
+{
   /* Set up the symbol hash table.  */
   _dl_setup_hash (l);
 
@@ -1510,7 +1419,7 @@ cannot enable executable stack as shared object requires");
       r->r_state = RT_ADD;
       _dl_debug_state ();
       LIBC_PROBE (map_start, 2, nsid, r);
-      make_consistent = true;
+      *make_consistent_p = true;
     }
   else
     assert (r->r_state == RT_ADD);
@@ -1520,266 +1429,430 @@ cannot enable executable stack as shared object requires");
   if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
     _dl_audit_objopen (l, nsid);
 #endif
-
-  return l;
 }
-\f
-/* Print search path.  */
-static void
-print_search_path (struct r_search_path_elem **list,
-		   const char *what, const char *name)
-{
-  char buf[max_dirnamelen + max_capstrlen];
-  int first = 1;
-
-  _dl_debug_printf (" search path=");
 
-  while (*list != NULL && (*list)->what == what) /* Yes, ==.  */
-    {
-      char *endp = __mempcpy (buf, (*list)->dirname, (*list)->dirnamelen);
-      size_t cnt;
+/* Map in the shared object NAME, actually located in REALNAME, and already
+   opened on FD.  */
 
-      for (cnt = 0; cnt < ncapstr; ++cnt)
-	if ((*list)->status[cnt] != nonexisting)
-	  {
-#ifdef SHARED
-	    char *cp = __mempcpy (endp, capstr[cnt].str, capstr[cnt].len);
-	    if (cp == buf || (cp == buf + 1 && buf[0] == '/'))
-	      cp[0] = '\0';
-	    else
-	      cp[-1] = '\0';
-#else
-	    *endp = '\0';
+#ifndef EXTERNAL_MAP_FROM_FD
+static
 #endif
-
-	    _dl_debug_printf_c (first ? "%s" : ":%s", buf);
-	    first = 0;
-	  }
-
-      ++list;
-    }
-
-  if (name != NULL)
-    _dl_debug_printf_c ("\t\t(%s from file %s)\n", what,
-			DSO_FILENAME (name));
-  else
-    _dl_debug_printf_c ("\t\t(%s)\n", what);
-}
-\f
-/* Open a file and verify it is an ELF file for this architecture.  We
-   ignore only ELF files for other architectures.  Non-ELF files and
-   ELF files with different header information cause fatal errors since
-   this could mean there is something wrong in the installation and the
-   user might want to know about this.
-
-   If FD is not -1, then the file is already open and FD refers to it.
-   In that case, FD is consumed for both successful and error returns.  */
-static int
-open_verify (const char *name, int fd,
-             struct filebuf *fbp, struct link_map *loader,
-	     int whatcode, int mode, bool *found_other_class, bool free_name)
+struct link_map *
+_dl_map_object_from_fd (const char *name, const char *origname, int fd,
+			struct filebuf *fbp, char *realname,
+			struct link_map *loader, int l_type, int mode,
+			void **stack_endp, Lmid_t nsid)
 {
-  /* This is the expected ELF header.  */
-#define ELF32_CLASS ELFCLASS32
-#define ELF64_CLASS ELFCLASS64
-#ifndef VALID_ELF_HEADER
-# define VALID_ELF_HEADER(hdr,exp,size)	(memcmp (hdr, exp, size) == 0)
-# define VALID_ELF_OSABI(osabi)		(osabi == ELFOSABI_SYSV)
-# define VALID_ELF_ABIVERSION(osabi,ver) (ver == 0)
-#elif defined MORE_ELF_HEADER_DATA
-  MORE_ELF_HEADER_DATA;
-#endif
-  static const unsigned char expected[EI_NIDENT] =
-  {
-    [EI_MAG0] = ELFMAG0,
-    [EI_MAG1] = ELFMAG1,
-    [EI_MAG2] = ELFMAG2,
-    [EI_MAG3] = ELFMAG3,
-    [EI_CLASS] = ELFW(CLASS),
-    [EI_DATA] = byteorder,
-    [EI_VERSION] = EV_CURRENT,
-    [EI_OSABI] = ELFOSABI_SYSV,
-    [EI_ABIVERSION] = 0
-  };
-  /* Initialize it to make the compiler happy.  */
+  struct link_map *l = NULL;
+  /* Initialize to keep the compiler happy.  */
   const char *errstring = NULL;
   int errval = 0;
+  struct r_debug *r = _dl_debug_update (nsid);
+  bool make_consistent = false;
 
-#ifdef SHARED
-  /* Give the auditing libraries a chance.  */
-  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+  /* Get file information.  To match the kernel behavior, do not fill
+     in this information for the executable in case of an explicit
+     loader invocation.  */
+  struct r_file_id id;
+  if (mode & __RTLD_OPENEXEC)
     {
-      const char *original_name = name;
-      name = _dl_audit_objsearch (name, loader, whatcode);
-      if (name == NULL)
-	return -1;
-
-      if (fd != -1 && name != original_name && strcmp (name, original_name))
+      assert (nsid == LM_ID_BASE);
+      memset (&id, 0, sizeof (id));
+    }
+  else
+    {
+      if (__glibc_unlikely (!_dl_get_file_id (fd, &id)))
 	{
-	  /* An audit library changed what we're supposed to open,
-	     so FD no longer matches it.  */
-	  __close_nocancel (fd);
-	  fd = -1;
+	  errstring = N_("cannot stat shared object");
+	lose_errno:
+	  errval = errno;
+	lose:
+	  /* The file might already be closed.  */
+	  if (fd != -1)
+	    __close_nocancel (fd);
+	  if (l != NULL && l->l_map_start != 0)
+	    _dl_unmap_segments (l);
+	  if (l != NULL && l->l_origin != (char *) -1l)
+	    free ((char *) l->l_origin);
+	  if (l != NULL && !l->l_libname->dont_free)
+	    free (l->l_libname);
+	  if (l != NULL && l->l_phdr_allocated)
+	    free ((void *) l->l_phdr);
+	  free (l);
+	  free (realname);
+
+	  if (make_consistent && r != NULL)
+	    {
+	      r->r_state = RT_CONSISTENT;
+	      _dl_debug_state ();
+	      LIBC_PROBE (map_failed, 2, nsid, r);
+	    }
+
+	  _dl_signal_error (errval, name, NULL, errstring);
 	}
+
+      /* Look again to see if the real name matched another already loaded.  */
+      for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
+	if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
+	  {
+	    /* The object is already loaded.
+	       Just bump its reference count and return it.  */
+	    __close_nocancel (fd);
+
+	    /* If the name is not in the list of names for this object add
+	       it.  */
+	    free (realname);
+	    add_name_to_object (l, name);
+
+	    return l;
+	  }
+    }
+
+#ifdef SHARED
+  /* When loading into a namespace other than the base one we must
+     avoid loading ld.so since there can only be one copy.  Ever.  */
+  if (__glibc_unlikely (nsid != LM_ID_BASE)
+      && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id)
+	  || _dl_name_match_p (name, &GL(dl_rtld_map))))
+    {
+      /* This is indeed ld.so.  Create a new link_map which refers to
+	 the real one for almost everything.  */
+      l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
+      if (l == NULL)
+	goto fail_new;
+
+      /* Refer to the real descriptor.  */
+      l->l_real = &GL(dl_rtld_map);
+
+      /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen().  */
+      l->l_addr = l->l_real->l_addr;
+      l->l_ld = l->l_real->l_ld;
+
+      /* No need to bump the refcount of the real object, ld.so will
+	 never be unloaded.  */
+      __close_nocancel (fd);
+
+      /* Add the map for the mirrored object to the object list.  */
+      _dl_add_to_namespace_list (l, nsid);
+
+      return l;
     }
 #endif
 
-  if (fd == -1)
-    /* Open the file.  We always open files read-only.  */
-    fd = __open64_nocancel (name, O_RDONLY | O_CLOEXEC);
+  if (mode & RTLD_NOLOAD)
+    {
+      /* We are not supposed to load the object unless it is already
+	 loaded.  So return now.  */
+      free (realname);
+      __close_nocancel (fd);
+      return NULL;
+    }
 
-  if (fd != -1)
+  /* Print debugging message.  */
+  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+    _dl_debug_printf ("file=%s [%lu];  generating link map\n", name, nsid);
+
+  /* Enter the new object in the list of loaded objects.  */
+  l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
+  if (__glibc_unlikely (l == NULL))
     {
-      ElfW(Ehdr) *ehdr;
-      ElfW(Phdr) *phdr;
-      size_t maplength;
-
-      /* We successfully opened the file.  Now verify it is a file
-	 we can use.  */
-      __set_errno (0);
-      fbp->len = 0;
-      assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
-      /* Read in the header.  */
-      do
-	{
-	  ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len,
-					    sizeof (fbp->buf) - fbp->len);
-	  if (retlen <= 0)
-	    break;
-	  fbp->len += retlen;
-	}
-      while (__glibc_unlikely (fbp->len < sizeof (ElfW(Ehdr))));
+#ifdef SHARED
+    fail_new:
+#endif
+      errstring = N_("cannot create shared object descriptor");
+      goto lose_errno;
+    }
 
-      /* This is where the ELF header is loaded.  */
-      ehdr = (ElfW(Ehdr) *) fbp->buf;
+  if (_ld_map_object_1 (l, &fd, fbp, mode, loader, stack_endp, &errval,
+                        &errstring, do_mmap, NULL))
+    goto lose;
 
-      /* Now run the tests.  */
-      if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr))))
-	{
-	  errval = errno;
-	  errstring = (errval == 0
+  /* We are done mapping in the file.  We no longer need the descriptor.  */
+  if (__glibc_unlikely (__close_nocancel (fd) != 0))
+    {
+      errstring = N_("cannot close file descriptor");
+      goto lose_errno;
+    }
+  /* Signal that we closed the file.  */
+  fd = -1;
+
+  _ld_map_object_2 (l, mode, id, origname, nsid, r, &make_consistent);
+  return l;
+}
+\f
+/* Print search path.  */
+static void
+print_search_path (struct r_search_path_elem **list,
+		   const char *what, const char *name)
+{
+  char buf[max_dirnamelen + max_capstrlen];
+  int first = 1;
+
+  _dl_debug_printf (" search path=");
+
+  while (*list != NULL && (*list)->what == what) /* Yes, ==.  */
+    {
+      char *endp = __mempcpy (buf, (*list)->dirname, (*list)->dirnamelen);
+      size_t cnt;
+
+      for (cnt = 0; cnt < ncapstr; ++cnt)
+	if ((*list)->status[cnt] != nonexisting)
+	  {
+#ifdef SHARED
+	    char *cp = __mempcpy (endp, capstr[cnt].str, capstr[cnt].len);
+	    if (cp == buf || (cp == buf + 1 && buf[0] == '/'))
+	      cp[0] = '\0';
+	    else
+	      cp[-1] = '\0';
+#else
+	    *endp = '\0';
+#endif
+
+	    _dl_debug_printf_c (first ? "%s" : ":%s", buf);
+	    first = 0;
+	  }
+
+      ++list;
+    }
+
+  if (name != NULL)
+    _dl_debug_printf_c ("\t\t(%s from file %s)\n", what,
+			DSO_FILENAME (name));
+  else
+    _dl_debug_printf_c ("\t\t(%s)\n", what);
+}
+\f
+
+static ssize_t
+do_pread (void *arg, void *buf, size_t count, off_t offset)
+{
+  int fd = *(const int *) arg;
+  return __pread64_nocancel (fd, buf, count, offset);
+}
+
+static ssize_t
+do_pread_memcpy (void *arg, void *buf, size_t count, off_t offset)
+{
+  struct const_fbuf *fb = arg;
+  if (offset >= fb->len)
+    return -1;
+  if (offset + count > fb->len)
+    count = fb->len - offset;
+  if (count)
+    memcpy (buf, fb->buf + offset, count);
+  return count;
+}
+
+static int
+do_open_verify (const char *name, void *fd,
+                struct filebuf *fbp, struct link_map *loader,
+                bool *found_other_class, bool free_name,
+                __typeof (do_pread) *__pread64_nocancel)
+{
+  /* This is the expected ELF header.  */
+#define ELF32_CLASS ELFCLASS32
+#define ELF64_CLASS ELFCLASS64
+#ifndef VALID_ELF_HEADER
+# define VALID_ELF_HEADER(hdr,exp,size)	(memcmp (hdr, exp, size) == 0)
+# define VALID_ELF_OSABI(osabi)		(osabi == ELFOSABI_SYSV)
+# define VALID_ELF_ABIVERSION(osabi,ver) (ver == 0)
+#elif defined MORE_ELF_HEADER_DATA
+  MORE_ELF_HEADER_DATA;
+#endif
+  static const unsigned char expected[EI_NIDENT] =
+  {
+    [EI_MAG0] = ELFMAG0,
+    [EI_MAG1] = ELFMAG1,
+    [EI_MAG2] = ELFMAG2,
+    [EI_MAG3] = ELFMAG3,
+    [EI_CLASS] = ELFW(CLASS),
+    [EI_DATA] = byteorder,
+    [EI_VERSION] = EV_CURRENT,
+    [EI_OSABI] = ELFOSABI_SYSV,
+    [EI_ABIVERSION] = 0
+  };
+  /* Initialize it to make the compiler happy.  */
+  const char *errstring = NULL;
+  int errval = 0;
+  ElfW(Ehdr) _ehdr;
+  ElfW(Ehdr) *ehdr = &_ehdr;;
+  ElfW(Phdr) *phdr;
+  size_t maplength;
+
+  /* We successfully opened the file.  Now verify it is a file
+   we can use.  */
+  __set_errno (0);
+  /* Read in the header.  */
+  if (__pread64_nocancel (fd, &_ehdr, sizeof(_ehdr), 0) != sizeof(_ehdr))
+    {
+      errval = errno;
+      errstring = (errval == 0
 		       ? N_("file too short") : N_("cannot read file data"));
-	lose:
-	  if (free_name)
-	    {
-	      char *realname = (char *) name;
-	      name = strdupa (realname);
-	      free (realname);
-	    }
-	  __close_nocancel (fd);
-	  _dl_signal_error (errval, name, NULL, errstring);
-	}
+    lose:
+      if (free_name)
+        {
+          char *realname = (char *) name;
+          name = strdupa (realname);
+          free (realname);
+        }
+      _dl_signal_error (errval, name, NULL, errstring);
+      return -1;
+    }
 
-      /* See whether the ELF header is what we expect.  */
-      if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected,
-						EI_ABIVERSION)
-			    || !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
-						      ehdr->e_ident[EI_ABIVERSION])
-			    || memcmp (&ehdr->e_ident[EI_PAD],
-				       &expected[EI_PAD],
-				       EI_NIDENT - EI_PAD) != 0))
-	{
-	  /* Something is wrong.  */
-	  const Elf32_Word *magp = (const void *) ehdr->e_ident;
-	  if (*magp !=
+  /* See whether the ELF header is what we expect.  */
+  if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected,
+					    EI_ABIVERSION)
+			|| !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
+						  ehdr->e_ident[EI_ABIVERSION])
+			|| memcmp (&ehdr->e_ident[EI_PAD],
+				   &expected[EI_PAD],
+				    EI_NIDENT - EI_PAD) != 0))
+    {
+      /* Something is wrong.  */
+      const Elf32_Word *magp = (const void *) ehdr->e_ident;
+      if (*magp !=
 #if BYTE_ORDER == LITTLE_ENDIAN
-	      ((ELFMAG0 << (EI_MAG0 * 8))
-	       | (ELFMAG1 << (EI_MAG1 * 8))
-	       | (ELFMAG2 << (EI_MAG2 * 8))
-	       | (ELFMAG3 << (EI_MAG3 * 8)))
+          ((ELFMAG0 << (EI_MAG0 * 8))
+           | (ELFMAG1 << (EI_MAG1 * 8))
+           | (ELFMAG2 << (EI_MAG2 * 8))
+           | (ELFMAG3 << (EI_MAG3 * 8)))
 #else
-	      ((ELFMAG0 << (EI_MAG3 * 8))
-	       | (ELFMAG1 << (EI_MAG2 * 8))
-	       | (ELFMAG2 << (EI_MAG1 * 8))
-	       | (ELFMAG3 << (EI_MAG0 * 8)))
+          ((ELFMAG0 << (EI_MAG3 * 8))
+           | (ELFMAG1 << (EI_MAG2 * 8))
+           | (ELFMAG2 << (EI_MAG1 * 8))
+           | (ELFMAG3 << (EI_MAG0 * 8)))
 #endif
-	      )
-	    errstring = N_("invalid ELF header");
+          )
+        errstring = N_("invalid ELF header");
 
-	  else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS))
-	    {
-	      /* This is not a fatal error.  On architectures where
-		 32-bit and 64-bit binaries can be run this might
-		 happen.  */
-	      *found_other_class = true;
-	      __close_nocancel (fd);
-	      __set_errno (ENOENT);
-	      return -1;
-	    }
-	  else if (ehdr->e_ident[EI_DATA] != byteorder)
-	    {
-	      if (BYTE_ORDER == BIG_ENDIAN)
-		errstring = N_("ELF file data encoding not big-endian");
-	      else
-		errstring = N_("ELF file data encoding not little-endian");
-	    }
-	  else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
-	    errstring
-	      = N_("ELF file version ident does not match current one");
-	  /* XXX We should be able so set system specific versions which are
-	     allowed here.  */
-	  else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI]))
-	    errstring = N_("ELF file OS ABI invalid");
-	  else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
-					  ehdr->e_ident[EI_ABIVERSION]))
-	    errstring = N_("ELF file ABI version invalid");
-	  else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD],
-			   EI_NIDENT - EI_PAD) != 0)
-	    errstring = N_("nonzero padding in e_ident");
-	  else
-	    /* Otherwise we don't know what went wrong.  */
-	    errstring = N_("internal error");
+      else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS))
+        {
+          /* This is not a fatal error.  On architectures where
+             32-bit and 64-bit binaries can be run this might
+             happen.  */
+          *found_other_class = true;
+          __set_errno (ENOENT);
+          return -1;
+        }
+      else if (ehdr->e_ident[EI_DATA] != byteorder)
+        {
+          if (BYTE_ORDER == BIG_ENDIAN)
+            errstring = N_("ELF file data encoding not big-endian");
+          else
+            errstring = N_("ELF file data encoding not little-endian");
+        }
+      else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
+        errstring
+          = N_("ELF file version ident does not match current one");
+      /* XXX We should be able so set system specific versions which are
+         allowed here.  */
+      else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI]))
+        errstring = N_("ELF file OS ABI invalid");
+      else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
+				      ehdr->e_ident[EI_ABIVERSION]))
+        errstring = N_("ELF file ABI version invalid");
+      else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD],
+		       EI_NIDENT - EI_PAD) != 0)
+        errstring = N_("nonzero padding in e_ident");
+      else
+        /* Otherwise we don't know what went wrong.  */
+        errstring = N_("internal error");
 
-	  goto lose;
-	}
+      goto lose;
+    }
 
-      if (__glibc_unlikely (ehdr->e_version != EV_CURRENT))
-	{
-	  errstring = N_("ELF file version does not match current one");
-	  goto lose;
-	}
-      if (! __glibc_likely (elf_machine_matches_host (ehdr)))
-	{
-	  __close_nocancel (fd);
-	  __set_errno (ENOENT);
-	  return -1;
-	}
-      else if (__glibc_unlikely (ehdr->e_type != ET_DYN
+  if (__glibc_unlikely (ehdr->e_version != EV_CURRENT))
+    {
+      errstring = N_("ELF file version does not match current one");
+      goto lose;
+    }
+  if (! __glibc_likely (elf_machine_matches_host (ehdr)))
+    {
+      __set_errno (ENOENT);
+      return -1;
+    }
+  else if (__glibc_unlikely (ehdr->e_type != ET_DYN
 				 && ehdr->e_type != ET_EXEC))
-	{
-	  errstring = N_("only ET_DYN and ET_EXEC can be loaded");
-	  goto lose;
-	}
-      else if (__glibc_unlikely (ehdr->e_phentsize != sizeof (ElfW(Phdr))))
-	{
-	  errstring = N_("ELF file's phentsize not the expected size");
-	  goto lose;
-	}
+    {
+      errstring = N_("only ET_DYN and ET_EXEC can be loaded");
+      goto lose;
+    }
+  else if (__glibc_unlikely (ehdr->e_phentsize != sizeof (ElfW(Phdr))))
+    {
+      errstring = N_("ELF file's phentsize not the expected size");
+      goto lose;
+    }
 
-      maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
-      if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
-	phdr = (void *) (fbp->buf + ehdr->e_phoff);
-      else
-	{
-	  phdr = alloca (maplength);
-	  if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
-					   ehdr->e_phoff) != maplength)
-	    {
-	      errval = errno;
-	      errstring = N_("cannot read file data");
-	      goto lose;
-	    }
-	}
+  maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
+  filebuf_ensure (fbp, maplength + ehdr->e_phoff);
+  if ((size_t) __pread64_nocancel (fd, fbp->buf, maplength +
+				   ehdr->e_phoff, 0) != maplength +
+				   ehdr->e_phoff)
+    {
+      errval = errno;
+      errstring = N_("cannot read file data");
+      goto lose;
+    }
+  fbp->len = maplength + ehdr->e_phoff;
+  phdr = (void *) (fbp->buf + ehdr->e_phoff);
+
+  if (__glibc_unlikely (elf_machine_reject_phdr_p
+		       (phdr, ehdr->e_phnum, fbp->buf, fbp->len,
+			loader, -1)))
+    {
+      __set_errno (ENOENT);
+      return -1;
+    }
+
+  return 0;
+}
+
+/* Open a file and verify it is an ELF file for this architecture.  We
+   ignore only ELF files for other architectures.  Non-ELF files and
+   ELF files with different header information cause fatal errors since
+   this could mean there is something wrong in the installation and the
+   user might want to know about this.
+
+   If FD is not -1, then the file is already open and FD refers to it.
+   In that case, FD is consumed for both successful and error returns.  */
+static int
+open_verify (const char *name, int fd,
+             struct filebuf *fbp, struct link_map *loader,
+             int whatcode, int mode, bool *found_other_class, bool free_name)
+{
+#ifdef SHARED
+  /* Give the auditing libraries a chance.  */
+  if (__glibc_unlikely (GLRO(dl_naudit) > 0))
+    {
+      const char *original_name = name;
+      name = _dl_audit_objsearch (name, loader, whatcode);
+      if (name == NULL)
+	return -1;
 
-      if (__glibc_unlikely (elf_machine_reject_phdr_p
-			    (phdr, ehdr->e_phnum, fbp->buf, fbp->len,
-			     loader, fd)))
+      if (fd != -1 && name != original_name && strcmp (name, original_name))
 	{
+	  /* An audit library changed what we're supposed to open,
+	     so FD no longer matches it.  */
 	  __close_nocancel (fd);
-	  __set_errno (ENOENT);
-	  return -1;
+	  fd = -1;
 	}
+    }
+#endif
 
+  if (fd == -1)
+    /* Open the file.  We always open files read-only.  */
+    fd = __open64_nocancel (name, O_RDONLY | O_CLOEXEC);
+
+  if (fd != -1)
+    {
+      int err = do_open_verify (name, &fd, fbp, loader,
+                                found_other_class,
+                                free_name, do_pread);
+      if (err)
+        {
+        __close_nocancel (fd);
+        return -1;
+        }
     }
 
   return fd;
@@ -1946,16 +2019,16 @@ open_path (const char *name, size_t namelen, int mode,
 
 /* Map in the shared object file NAME.  */
 
-struct link_map *
-_dl_map_object (struct link_map *loader, const char *name,
-		int type, int trace_mode, int mode, Lmid_t nsid)
+static struct link_map *
+___dl_map_object (struct link_map *loader, const char *name,
+		  int type, int trace_mode, int mode, Lmid_t nsid,
+		  struct filebuf *fbp)
 {
   int fd;
   const char *origname = NULL;
   char *realname;
   char *name_copy;
   struct link_map *l;
-  struct filebuf fb;
 
   assert (nsid >= 0);
   assert (nsid < GL(dl_nns));
@@ -2045,7 +2118,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	      {
 		fd = open_path (name, namelen, mode,
 				&l->l_rpath_dirs,
-				&realname, &fb, loader, LA_SER_RUNPATH,
+				&realname, fbp, loader, LA_SER_RUNPATH,
 				&found_other_class);
 		if (fd != -1)
 		  break;
@@ -2061,7 +2134,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 			      "RPATH"))
 	    fd = open_path (name, namelen, mode,
 			    &main_map->l_rpath_dirs,
-			    &realname, &fb, loader ?: main_map, LA_SER_RUNPATH,
+			    &realname, fbp, loader ?: main_map, LA_SER_RUNPATH,
 			    &found_other_class);
 
 	  /* Also try DT_RUNPATH in the executable for LD_AUDIT dlopen
@@ -2075,7 +2148,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	      if (cache_rpath (main_map, &l_rpath_dirs,
 			       DT_RUNPATH, "RUNPATH"))
 		fd = open_path (name, namelen, mode, &l_rpath_dirs,
-				&realname, &fb, loader ?: main_map,
+				&realname, fbp, loader ?: main_map,
 				LA_SER_RUNPATH, &found_other_class);
 	    }
 	}
@@ -2083,7 +2156,7 @@ _dl_map_object (struct link_map *loader, const char *name,
       /* Try the LD_LIBRARY_PATH environment variable.  */
       if (fd == -1 && __rtld_env_path_list.dirs != (void *) -1)
 	fd = open_path (name, namelen, mode, &__rtld_env_path_list,
-			&realname, &fb,
+			&realname, fbp,
 			loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
 			LA_SER_LIBPATH, &found_other_class);
 
@@ -2092,7 +2165,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	  && cache_rpath (loader, &loader->l_runpath_dirs,
 			  DT_RUNPATH, "RUNPATH"))
 	fd = open_path (name, namelen, mode,
-			&loader->l_runpath_dirs, &realname, &fb, loader,
+			&loader->l_runpath_dirs, &realname, fbp, loader,
 			LA_SER_RUNPATH, &found_other_class);
 
       if (fd == -1)
@@ -2101,7 +2174,7 @@ _dl_map_object (struct link_map *loader, const char *name,
           if (realname != NULL)
             {
               fd = open_verify (realname, fd,
-                                &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+                                fbp, loader ?: GL(dl_ns)[nsid]._ns_loaded,
                                 LA_SER_CONFIG, mode, &found_other_class,
                                 false);
               if (fd == -1)
@@ -2155,7 +2228,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	      if (cached != NULL)
 		{
 		  fd = open_verify (cached, -1,
-				    &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+				    fbp, loader ?: GL(dl_ns)[nsid]._ns_loaded,
 				    LA_SER_CONFIG, mode, &found_other_class,
 				    false);
 		  if (__glibc_likely (fd != -1))
@@ -2173,7 +2246,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	      || __glibc_likely (!(l->l_flags_1 & DF_1_NODEFLIB)))
 	  && __rtld_search_dirs.dirs != (void *) -1)
 	fd = open_path (name, namelen, mode, &__rtld_search_dirs,
-			&realname, &fb, l, LA_SER_DEFAULT, &found_other_class);
+			&realname, fbp, l, LA_SER_DEFAULT, &found_other_class);
 
       /* Add another newline when we are tracing the library loading.  */
       if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
@@ -2189,7 +2262,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	fd = -1;
       else
 	{
-	  fd = open_verify (realname, -1, &fb,
+	  fd = open_verify (realname, -1, fbp,
 			    loader ?: GL(dl_ns)[nsid]._ns_loaded, 0, mode,
 			    &found_other_class, true);
 	  if (__glibc_unlikely (fd == -1))
@@ -2250,10 +2323,166 @@ _dl_map_object (struct link_map *loader, const char *name,
     }
 
   void *stack_end = __libc_stack_end;
-  return _dl_map_object_from_fd (name, origname, fd, &fb, realname, loader,
+  return _dl_map_object_from_fd (name, origname, fd, fbp, realname, loader,
 				 type, mode, &stack_end, nsid);
 }
 
+struct link_map *
+__dl_map_object (struct link_map *loader, const char *name,
+                 void *private, int type, int trace_mode,
+                 int mode, Lmid_t nsid)
+{
+  struct link_map *ret;
+  struct filebuf fb = {};
+
+  ret = ___dl_map_object (loader, name, type, trace_mode, mode, nsid, &fb);
+  filebuf_done (&fb);
+  return ret;
+}
+
+struct link_map *
+_dl_map_object (struct link_map *loader, const char *name,
+                int type, int trace_mode, int mode, Lmid_t nsid)
+{
+  return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid);
+}
+
+static void *
+do_mmapcpy (void *addr, size_t length, int prot, int flags,
+            void *arg, off_t offset, void *mapstart)
+{
+  const struct const_fbuf *fb = arg;
+  void *ret;
+
+  assert (flags & MAP_FIXED);
+  assert (addr >= mapstart);
+  if (fb->fd == -1)
+    ret = __mmap (addr, length, PROT_READ | PROT_WRITE,
+                  MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+  else
+    ret = __mmap (addr, length, PROT_READ | PROT_WRITE,
+                  MAP_SHARED | MAP_FIXED, fb->fd, addr - mapstart);
+  if (ret == MAP_FAILED)
+    return ret;
+  if (offset < fb->len)
+    {
+      size_t to_copy = length;
+      if (offset + to_copy > fb->len)
+        to_copy = fb->len - offset;
+      memcpy (ret, fb->buf + offset, to_copy);
+    }
+  if (__mprotect (ret, length, prot) == -1)
+    return MAP_FAILED;
+  return ret;
+}
+
+static void
+do_dlmem_premap (struct link_map *l, void *arg, size_t maplength)
+{
+#ifdef SHARED
+  struct const_fbuf *fb = arg;
+
+  fb->fd = _dl_audit_premap_dlmem (l, maplength);
+#endif
+}
+
+static struct link_map *
+___dl_map_object_from_mem (struct link_map *loader, const char *name,
+			   void *private, int type, int trace_mode,
+			   int mode, Lmid_t nsid, struct filebuf *fbp)
+{
+  struct link_map *l;
+  int err;
+  /* Initialize to keep the compiler happy.  */
+  const char *errstring = NULL;
+  int errval = 0;
+  struct r_debug *r = _dl_debug_update (nsid);
+  bool make_consistent = false;
+  struct r_file_id id = {};
+
+  assert (nsid >= 0);
+  assert (nsid < GL(dl_nns));
+
+  /* Will be true if we found a DSO which is of the other ELF class.  */
+  bool found_other_class = false;
+
+  err = do_open_verify (name, private, fbp,
+                        loader ?: GL(dl_ns)[nsid]._ns_loaded,
+                        &found_other_class, false, do_pread_memcpy);
+  if (err)
+    return NULL;
+
+  /* In case the LOADER information has only been provided to get to
+     the appropriate RUNPATH/RPATH information we do not need it
+     anymore.  */
+  if (mode & __RTLD_CALLMAP)
+    loader = NULL;
+
+  if (mode & RTLD_NOLOAD)
+    {
+      /* We are not supposed to load the object unless it is already
+	 loaded.  So return now.  */
+      return NULL;
+    }
+
+  /* Print debugging message.  */
+  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+    _dl_debug_printf ("dlmem [%lu];  generating link map\n", nsid);
+
+  /* Enter the new object in the list of loaded objects.  */
+  l = _dl_new_object ((char *) name, name, type, loader, mode, nsid);
+  if (__glibc_unlikely (l == NULL))
+    {
+      errstring = N_("cannot create shared object descriptor");
+      goto lose_errno;
+    }
+
+  void *stack_end = __libc_stack_end;
+  if (_ld_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval,
+                        &errstring, do_mmapcpy, do_dlmem_premap))
+    goto lose;
+
+  _ld_map_object_2 (l, mode, id, NULL, nsid, r, &make_consistent);
+  return l;
+
+lose_errno:
+  errval = errno;
+lose:
+  if (l != NULL && l->l_map_start != 0)
+    _dl_unmap_segments (l);
+  if (l != NULL && l->l_origin != (char *) -1l)
+    free ((char *) l->l_origin);
+  if (l != NULL && !l->l_libname->dont_free)
+    free (l->l_libname);
+  if (l != NULL && l->l_phdr_allocated)
+    free ((void *) l->l_phdr);
+  free (l);
+
+  if (make_consistent && r != NULL)
+    {
+      r->r_state = RT_CONSISTENT;
+      _dl_debug_state ();
+      LIBC_PROBE (map_failed, 2, nsid, r);
+    }
+
+  _dl_signal_error (errval, NULL, NULL, errstring);
+  return NULL;
+}
+
+struct link_map *
+__dl_map_object_from_mem (struct link_map *loader, const char *name,
+			  void *private, int type, int trace_mode,
+			  int mode, Lmid_t nsid)
+{
+  struct link_map *ret;
+  struct filebuf fb = {};
+
+  ret = ___dl_map_object_from_mem (loader, name, private, type, trace_mode,
+                                  mode, nsid, &fb);
+  filebuf_done (&fb);
+  return ret;
+}
+
 struct add_path_state
 {
   bool counting;
diff --git a/elf/dl-load.h b/elf/dl-load.h
index ecf6910c68..4c063f5463 100644
--- a/elf/dl-load.h
+++ b/elf/dl-load.h
@@ -100,6 +100,9 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
                                       - c->mapoff);
 }
 
+static void *
+do_mmap (void *addr, size_t length, int prot, int flags,
+         void *arg, off_t offset, void *mapstart);
 
 /* This is a subroutine of _dl_map_object_from_fd.  It is responsible
    for filling in several fields in *L: l_map_start, l_map_end, l_addr,
@@ -113,13 +116,14 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
    The file <dl-map-segments.h> defines this function.  The canonical
    implementation in elf/dl-map-segments.h might be replaced by a sysdeps
    version.  */
-static const char *_dl_map_segments (struct link_map *l, int fd,
+static const char *_dl_map_segments (struct link_map *l, void *fd,
                                      const ElfW(Ehdr) *header, int type,
                                      const struct loadcmd loadcmds[],
                                      size_t nloadcmds,
                                      const size_t maplength,
                                      bool has_holes,
-                                     struct link_map *loader);
+                                     struct link_map *loader,
+                                     __typeof (do_mmap) *m_map);
 
 /* All the error message strings _dl_map_segments might return are
    listed here so that different implementations in different sysdeps
diff --git a/elf/dl-main.h b/elf/dl-main.h
index 92766d06b4..d6be25aa48 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -104,6 +104,27 @@ struct dl_main_state
   bool version_info;
 };
 
+struct const_fbuf
+{
+  ssize_t len;
+  const unsigned char *buf;
+  int fd;
+};
+
+/* Open the shared object NAME and map in its segments.
+   LOADER's DT_RPATH is used in searching for NAME.
+   If the object is already opened, returns its existing map.  */
+extern struct link_map *
+__dl_map_object (struct link_map *loader,
+		 const char *name, void *private,
+		 int type, int trace_mode, int mode,
+		 Lmid_t nsid) attribute_hidden;
+extern struct link_map *
+__dl_map_object_from_mem (struct link_map *loader,
+			  const char *name, void *private,
+			  int type, int trace_mode, int mode,
+			  Lmid_t nsid) attribute_hidden;
+
 /* Helper function to invoke _dl_init_paths with the right arguments
    from *STATE.  */
 static inline void
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 504cfc0a41..89c0833544 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -19,15 +19,23 @@
 
 #include <dl-load.h>
 
+static void *
+do_mmap (void *addr, size_t length, int prot, int flags,
+         void *arg, off_t offset, void *mapstart)
+{
+  int fd = *(const int *) arg;
+  return __mmap (addr, length, prot, flags, fd, offset);
+}
+
 /* Map a segment and align it properly.  */
 
 static __always_inline ElfW(Addr)
 _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref,
-		 const size_t maplength, int fd)
+		 const size_t maplength)
 {
   if (__glibc_likely (c->mapalign <= GLRO(dl_pagesize)))
     return (ElfW(Addr)) __mmap ((void *) mappref, maplength, c->prot,
-				MAP_COPY|MAP_FILE, fd, c->mapoff);
+				MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
 
   /* If the segment alignment > the page size, allocate enough space to
      ensure that the segment can be properly aligned.  */
@@ -44,8 +52,8 @@ _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref,
   ElfW(Addr) map_start_aligned = ALIGN_UP (map_start, c->mapalign);
   map_start_aligned = (ElfW(Addr)) __mmap ((void *) map_start_aligned,
 					   maplength, c->prot,
-					   MAP_COPY|MAP_FILE|MAP_FIXED,
-					   fd, c->mapoff);
+					   MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED,
+					   -1, 0);
   if (__glibc_unlikely ((void *) map_start_aligned == MAP_FAILED))
     __munmap ((void *) map_start, maplen);
   else
@@ -72,11 +80,11 @@ _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref,
    other use of those parts of the address space).  */
 
 static __always_inline const char *
-_dl_map_segments (struct link_map *l, int fd,
+_dl_map_segments (struct link_map *l, void *fd,
                   const ElfW(Ehdr) *header, int type,
                   const struct loadcmd loadcmds[], size_t nloadcmds,
                   const size_t maplength, bool has_holes,
-                  struct link_map *loader)
+                  struct link_map *loader, __typeof (do_mmap) *m_map)
 {
   const struct loadcmd *c = loadcmds;
 
@@ -98,13 +106,18 @@ _dl_map_segments (struct link_map *l, int fd,
            - MAP_BASE_ADDR (l));
 
       /* Remember which part of the address space this object uses.  */
-      l->l_map_start = _dl_map_segment (c, mappref, maplength, fd);
+      l->l_map_start = _dl_map_segment (c, mappref, maplength);
       if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
         return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
 
       l->l_map_end = l->l_map_start + maplength;
       l->l_addr = l->l_map_start - c->mapstart;
 
+      if (m_map ((void *) l->l_map_start, maplength, c->prot,
+                 MAP_FIXED|MAP_COPY|MAP_FILE, fd, c->mapoff,
+                 (void *) l->l_map_start) == MAP_FAILED)
+        return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
+
       if (has_holes)
         {
           /* Change protection on the excess portion to disallow all access;
@@ -136,10 +149,10 @@ _dl_map_segments (struct link_map *l, int fd,
     {
       if (c->mapend > c->mapstart
           /* Map the segment contents from the file.  */
-          && (__mmap ((void *) (l->l_addr + c->mapstart),
+          && (m_map ((void *) (l->l_addr + c->mapstart),
                       c->mapend - c->mapstart, c->prot,
                       MAP_FIXED|MAP_COPY|MAP_FILE,
-                      fd, c->mapoff)
+                      fd, c->mapoff, (void *) l->l_map_start)
               == MAP_FAILED))
         return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
 
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 91a2d8a538..9ec35ac1c3 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -40,6 +40,7 @@
 
 #include <dl-dst.h>
 #include <dl-prop.h>
+#include <dl-main.h>
 
 
 /* We must be careful not to leave us in an inconsistent state.  Thus we
@@ -48,6 +49,7 @@
 struct dl_open_args
 {
   const char *file;
+  void *private;
   int mode;
   /* This is the caller of the dlopen() function.  */
   const void *caller_dlopen;
@@ -55,6 +57,10 @@ struct dl_open_args
   /* Namespace ID.  */
   Lmid_t nsid;
 
+  struct link_map *
+  (*dl_map) (struct link_map *loader, const char *name, void *private,
+              int type, int trace_mode, int mode, Lmid_t nsid);
+
   /* Original value of _ns_global_scope_pending_adds.  Set by
      dl_open_worker.  Only valid if nsid is a real namespace
      (non-negative).  */
@@ -531,7 +537,7 @@ dl_open_worker_begin (void *a)
 
   /* Load the named object.  */
   struct link_map *new;
-  args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,
+  args->map = new = args->dl_map (call_map, file, args->private, lt_loaded, 0,
 				    mode | __RTLD_CALLMAP, args->nsid);
 
   /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is
@@ -818,9 +824,11 @@ dl_open_worker (void *a)
 		      new->l_name, new->l_ns, new->l_direct_opencount);
 }
 
-void *
-_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
-	  int argc, char *argv[], char *env[])
+static void *
+do_dl_open (const char *file, void *private, int mode,
+            const void *caller_dlopen, Lmid_t nsid,
+	    int argc, char *argv[], char *env[],
+	    __typeof (__dl_map_object) *dl_map)
 {
   if ((mode & RTLD_BINDING_MASK) == 0)
     /* One of the flags must be set.  */
@@ -870,10 +878,12 @@ no more namespaces available for dlmopen()"));
 
   struct dl_open_args args;
   args.file = file;
+  args.private = private;
   args.mode = mode;
   args.caller_dlopen = caller_dlopen;
   args.map = NULL;
   args.nsid = nsid;
+  args.dl_map = dl_map;
   /* args.libc_already_loaded is always assigned by dl_open_worker
      (before any explicit/non-local returns).  */
   args.argc = argc;
@@ -935,6 +945,25 @@ no more namespaces available for dlmopen()"));
   return args.map;
 }
 
+void *
+_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
+	  int argc, char *argv[], char *env[])
+{
+  return do_dl_open (file, NULL, mode, caller_dlopen, nsid, argc, argv, env,
+                     __dl_map_object);
+}
+
+void *
+_dl_mem (const unsigned char *buffer, size_t size, int mode,
+	 const void *caller_dlopen, Lmid_t nsid,
+	 int argc, char *argv[], char *env[])
+{
+  struct const_fbuf fb = { .buf = buffer, .len = size, .fd = -1 };
+
+  return do_dl_open ("", &fb, mode, caller_dlopen, nsid, argc, argv, env,
+                     __dl_map_object_from_mem);
+}
+
 
 void
 _dl_show_scope (struct link_map *l, int from)
diff --git a/elf/link.h b/elf/link.h
index 3b5954d981..b59038c680 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -193,6 +193,7 @@ extern unsigned int la_version (unsigned int __version);
 extern void la_activity (uintptr_t *__cookie, unsigned int __flag);
 extern char *la_objsearch (const char *__name, uintptr_t *__cookie,
 			   unsigned int __flag);
+extern int la_premap_dlmem (size_t maplength, uintptr_t *__cookie);
 extern unsigned int la_objopen (struct link_map *__map, Lmid_t __lmid,
 				uintptr_t *__cookie);
 extern void la_preinit (uintptr_t *__cookie);
diff --git a/elf/rtld.c b/elf/rtld.c
index f82fbeb132..ce35d0a28b 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -370,6 +370,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_mcount = _dl_mcount,
     ._dl_lookup_symbol_x = _dl_lookup_symbol_x,
     ._dl_open = _dl_open,
+    ._dl_mem = _dl_mem,
     ._dl_close = _dl_close,
     ._dl_catch_error = _dl_catch_error,
     ._dl_error_free = _dl_error_free,
@@ -989,7 +990,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
       return;
     }
 
-  enum { naudit_ifaces = 8 };
+  enum { naudit_ifaces = 9 };
   union
   {
     struct audit_ifaces ifaces;
@@ -1003,6 +1004,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
   static const char audit_iface_names[] =
     "la_activity\0"
     "la_objsearch\0"
+    "la_premap_dlmem\0"
     "la_objopen\0"
     "la_preinit\0"
     LA_SYMBIND "\0"
diff --git a/elf/tst-audit-dlmem.c b/elf/tst-audit-dlmem.c
new file mode 100644
index 0000000000..87c6d3741e
--- /dev/null
+++ b/elf/tst-audit-dlmem.c
@@ -0,0 +1,229 @@
+/* Check DT_AUDIT with dlmem.
+   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 <fcntl.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <sys/mman.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 },
+
+/* test value */
+#define TEST_BAR_VAL 123
+
+void *dlmem (const unsigned char *buffer, size_t size, int flags);
+
+static void *
+_dlmem_wrapper (const char *file, unsigned flags)
+{
+  off_t len;
+  int fd;
+  void *handle;
+  void *addr;
+
+  fd = open (file, O_RDONLY);
+  if (fd == -1)
+    {
+      printf ("cannot open %s, %s\n", file, strerror(errno));
+      exit (EXIT_FAILURE);
+    }
+  len = lseek (fd, 0, SEEK_END);
+  lseek (fd, 0, SEEK_SET);
+  addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (addr == MAP_FAILED)
+    {
+      printf ("cannot mmap %s, %s\n", file, strerror(errno));
+      exit (EXIT_FAILURE);
+    }
+  handle = dlmem (addr, len, RTLD_NOW);
+  if (handle == NULL)
+    {
+      printf ("cannot dlmem, %s\n", strerror(errno));
+      exit (EXIT_FAILURE);
+    }
+  munmap (addr, len);
+  close (fd);
+  return handle;
+}
+
+#define dlmem_wrapper(n, f) _dlmem_wrapper (BUILDDIR n, f)
+
+static int
+handle_restart (void)
+{
+  {
+    void *h = dlmem_wrapper ("../" LIBC_SO, RTLD_NOW);
+
+    pid_t (*s) (void) = xdlsym (h, "getpid");
+    TEST_COMPARE (s (), getpid ());
+
+    xdlclose (h);
+  }
+
+  {
+    Dl_info info;
+    void *h = dlmem_wrapper ("tst-audit18mod.so", RTLD_NOW);
+
+    int (*foo) (void) = xdlsym (h, "foo");
+    TEST_COMPARE (foo (), 10);
+
+    int *bar = xdlsym (h, "bar");
+    TEST_COMPARE (*bar, 35);
+
+    if (!dladdr (bar, &info))
+      {
+        printf ("dladdr failed\n");
+        exit (EXIT_FAILURE);
+      }
+    fprintf (stderr, "offset bar %lx\n", info.dli_saddr - info.dli_fbase);
+    /* write another value for parent to read */
+    *bar = TEST_BAR_VAL;
+  }
+
+  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;
+
+  setenv ("LD_AUDIT", "tst-auditmod-dlmem.so", 0);
+  struct support_capture_subprocess result
+    = support_capture_subprogram (spargv[0], spargv);
+  support_capture_subprocess_check (&result, "tst-auditdlmem", 0,
+                                    sc_allow_stderr);
+
+  struct
+  {
+    const char *name;
+    bool found;
+  } audit_iface[] =
+  {
+    { "la_version", false },
+    { "la_objsearch", false },
+    { "la_activity", false },
+    { "la_premap_dlmem", false },
+    { "la_objopen", false },
+    { "la_objclose", false },
+    { "la_preinit", false },
+#if __WORDSIZE == 32
+    { "la_symbind32", false },
+#elif __WORDSIZE == 64
+    { "la_symbind64", false },
+#endif
+  };
+
+  size_t maplength = 0;
+  char shm_name[256] = {};
+  const char *ml = "maplength";
+  int fd;
+  void *addr;
+  const char *off_str = "offset";
+  char sym_name[256] = {};
+  unsigned long sym_offset = 0;
+  int *sym;
+
+  /* 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;
+      if (strncmp (buffer, ml, strlen (ml)) == 0)
+	sscanf(buffer, "%*s %zi %255s", &maplength, shm_name);
+      if (strncmp (buffer, off_str, strlen (off_str)) == 0)
+	sscanf(buffer, "%*s %255s %lx", sym_name, &sym_offset);
+    }
+  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);
+
+  if (!maplength || !shm_name[0])
+    {
+      printf ("premap_dlmem didn't work\n");
+      exit (EXIT_FAILURE);
+    }
+  if (!sym_offset)
+    {
+      printf ("sym_offset not found\n");
+      exit (EXIT_FAILURE);
+    }
+  printf ("maplength=%zi shm_name=%s\n", maplength, shm_name);
+  fd = shm_open (shm_name, O_RDWR, 0);
+  if (fd == -1)
+    {
+      printf ("cannot open shm\n");
+      exit (EXIT_FAILURE);
+    }
+  shm_unlink (shm_name);
+  addr = mmap (NULL, maplength, PROT_READ | PROT_WRITE | PROT_EXEC,
+               MAP_SHARED, fd, 0);
+  if (addr == MAP_FAILED)
+    {
+      printf ("cannot mmap shm\n");
+      exit (EXIT_FAILURE);
+    }
+  sym = addr + sym_offset;
+  TEST_COMPARE (*sym, TEST_BAR_VAL);
+
+  munmap (addr, maplength);
+  return 0;
+}
+
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
diff --git a/elf/tst-audit18mod.c b/elf/tst-audit18mod.c
index f010a475dc..2bde8788eb 100644
--- a/elf/tst-audit18mod.c
+++ b/elf/tst-audit18mod.c
@@ -21,3 +21,5 @@ foo (void)
 {
   return 10;
 }
+
+int bar = 35;
diff --git a/elf/tst-auditmod-dlmem.c b/elf/tst-auditmod-dlmem.c
new file mode 100644
index 0000000000..bd413da436
--- /dev/null
+++ b/elf/tst-auditmod-dlmem.c
@@ -0,0 +1,102 @@
+/* Check DT_AUDIT with dlmem.
+   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 <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+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__);
+  return (char *) name;
+}
+
+void
+la_activity (uintptr_t *cookie, unsigned int flag)
+{
+  fprintf (stderr, "%s\n", __func__);
+}
+
+int
+la_premap_dlmem (size_t maplength, uintptr_t *cookie)
+{
+  int fd;
+  int err;
+  const char *shm_name = "/tst-dlmem";
+
+  fprintf (stderr, "%s\n", __func__);
+  fd = shm_open (shm_name, O_RDWR | O_CREAT | O_TRUNC, 0600);
+  if (fd == -1)
+    {
+      perror ("shm_open()");
+      return -1;
+    }
+  err = ftruncate (fd, maplength);
+  if (err)
+    {
+      perror ("ftruncate()");
+      return -1;
+    }
+
+  fprintf (stderr, "maplength %zi %s\n", maplength, shm_name);
+  return fd;
+}
+
+unsigned int
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+}
+
+unsigned int
+la_objclose (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  return 0;
+}
+
+void
+la_preinit (uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+}
+
+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/include/dlfcn.h b/include/dlfcn.h
index ae25f05303..e09cb8cc72 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -100,6 +100,8 @@ struct dlfcn_hook
 {
   /* Public interfaces.  */
   void *(*dlopen) (const char *file, int mode, void *dl_caller);
+  void *(*dlmem) (const unsigned char *buffer, size_t size, int mode,
+		  void *dl_caller);
   int (*dlclose) (void *handle);
   void *(*dlsym) (void *handle, const char *name, void *dl_caller);
   void *(*dlvsym) (void *handle, const char *name, const char *version,
@@ -123,6 +125,8 @@ struct dlfcn_hook
    the __libc_dl* functions defined in elf/dl-libc.c instead.  */
 
 extern void *__dlopen (const char *file, int mode, void *caller);
+extern void *__dlmem (const unsigned char *file, size_t size, int mode,
+		      void *caller);
 extern void *__dlmopen (Lmid_t nsid, const char *file, int mode,
 			void *dl_caller);
 extern int __dlclose (void *handle);
diff --git a/manual/dynlink.texi b/manual/dynlink.texi
index 6a4a50d3f0..21bc6c067c 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 dlmem
 @c dlmopen
 @c dlopen
 @c dlsym
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c99dad77cc..26000e1e81 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -239,6 +239,7 @@ struct audit_ifaces
 {
   void (*activity) (uintptr_t *, unsigned int);
   char *(*objsearch) (const char *, uintptr_t *, unsigned int);
+  int (*premap_dlmem) (size_t, uintptr_t *);
   unsigned int (*objopen) (struct link_map *, Lmid_t, uintptr_t *);
   void (*preinit) (uintptr_t *);
   union
@@ -669,6 +670,9 @@ struct rtld_global_ro
 				   struct link_map *);
   void *(*_dl_open) (const char *file, int mode, const void *caller_dlopen,
 		     Lmid_t nsid, int argc, char *argv[], char *env[]);
+  void *(*_dl_mem) (const unsigned char *buffer, size_t size, int mode,
+		    const void *caller_dlopen,
+		    Lmid_t nsid, int argc, char *argv[], char *env[]);
   void (*_dl_close) (void *map);
   /* libdl in a secondary namespace (after dlopen) must use
      _dl_catch_error from the main namespace, so it has to be
@@ -1248,6 +1252,10 @@ extern char *_dl_dst_substitute (struct link_map *l, const char *name,
 extern void *_dl_open (const char *name, int mode, const void *caller,
 		       Lmid_t nsid, int argc, char *argv[], char *env[])
      attribute_hidden;
+extern void *_dl_mem (const unsigned char *buffer, size_t size, int mode,
+		      const void *caller,
+		      Lmid_t nsid, int argc, char *argv[], char *env[])
+     attribute_hidden;
 
 /* Free or queue for freeing scope OLD.  If other threads might be
    in the middle of _dl_fixup, _dl_profile_fixup or dl*sym using the
@@ -1360,6 +1368,11 @@ void _dl_audit_activity_map (struct link_map *l, int action)
 void _dl_audit_activity_nsid (Lmid_t nsid, int action)
   attribute_hidden;
 
+/* Call la_premap_dlmem from the audit modules from the link map and
+   get back an fd for dlmem(), or -1 for anonymous space.  */
+int _dl_audit_premap_dlmem (struct link_map *l, size_t maplength)
+  attribute_hidden;
+
 /* Call the la_objopen from the audit modules for the link_map L on the
    namespace identification NSID.  */
 void _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 4e3200ef55..db4ef39333 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 dlmem 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..9baf6cf92c 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 dlmem F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index f918bb2d48..364935fa51 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 dlmem 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..d820edf258 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 dlmem F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index f28402fe03..01109f19c8 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 dlmem 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..5f9d669b6d 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 dlmem 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..02b9425c89 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 dlmem F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 6450e17ebe..8e62d22eb2 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 dlmem 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..80d09fe3ca 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 dlmem 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..9558053778 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 dlmem 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..a1c5159b35 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 dlmem F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 16243a7a92..6f88cb7601 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 dlmem 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..7d4f3ced11 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 dlmem 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..6ba84f7881 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 dlmem F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 37178c503f..0ce48a4126 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 dlmem 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..b8a47b300c 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 dlmem 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..5c3eaee44e 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 dlmem 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..aa17ef60dd 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 dlmem 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..2dfd4321c7 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 dlmem 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..3ffe7b6530 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 dlmem F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index ffdb8819d5..2cf1bc4df7 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 dlmem 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..1291a13c3d 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 dlmem 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..491abbf990 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 dlmem 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..2c78254f0d 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 dlmem 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..eeb53b3bf7 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 dlmem F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index ff90d1bff2..4a3c25b9ab 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 dlmem F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index f1017f6ec5..70215e8479 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 dlmem 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..abda2933b4 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 dlmem 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..ba73fa4be0 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 dlmem 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..61f475d5c2 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 dlmem 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..a3b14edbfc 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 dlmem 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..034e1bf1a7 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 dlmem 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..f97c995f45 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 dlmem 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..441f397cb0 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 dlmem 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..b24093a386 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 dlmem F
-- 
2.37.2


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

* [PATCH 3/3] elf/dl-audit: add _dl_audit_premap fn [BZ #30007]
  2023-02-15 16:55 [PATCH v7 0/3] implement dlmem() with audit extensions Stas Sergeev
  2023-02-15 16:55 ` [PATCH 1/3] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
  2023-02-15 16:55 ` [PATCH 2/3] dlfcn,elf: implement dlmem() and audit [BZ #11767] Stas Sergeev
@ 2023-02-15 16:55 ` Stas Sergeev
  2 siblings, 0 replies; 7+ messages in thread
From: Stas Sergeev @ 2023-02-15 16:55 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch adds _dl_audit_premap() function that calls la_premap
audit call-back. That call-back takes the preferred map address and
map length, and gives back the address to where the solib should
actually be mapped.

In tst-auditmod-dlmem.c add such call-back as an example and use
MAP_32BIT flag in it to force the mapping into the low 4G of address
space.
Also add it to tst-auditmod18.c as dummy and make sure it is called.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-audit.c             | 22 ++++++++++++++++++++++
 elf/dl-map-segments.h      | 22 ++++++++++++++++++----
 elf/link.h                 |  2 ++
 elf/rtld.c                 |  3 ++-
 elf/tst-audit-dlmem.c      | 23 +++++++++++++++++++++++
 elf/tst-audit18.c          |  1 +
 elf/tst-auditmod-dlmem.c   | 16 ++++++++++++++++
 elf/tst-auditmod18.c       |  8 ++++++++
 sysdeps/generic/ldsodefs.h |  6 ++++++
 9 files changed, 98 insertions(+), 5 deletions(-)

diff --git a/elf/dl-audit.c b/elf/dl-audit.c
index 76bf365168..de3f6e8013 100644
--- a/elf/dl-audit.c
+++ b/elf/dl-audit.c
@@ -94,6 +94,28 @@ _dl_audit_premap_dlmem (struct link_map *l, size_t maplength)
    return -1;
 }
 
+void *
+_dl_audit_premap (struct link_map *l, void *pref_addr, size_t maplength)
+{
+  if (__glibc_likely (GLRO(dl_naudit) == 0))
+    return MAP_FAILED;
+
+  struct audit_ifaces *afct = GLRO(dl_audit);
+  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+    {
+      if (afct->premap != NULL)
+	{
+	  struct auditstate *state = link_map_audit_state (l, cnt);
+	  void *addr = afct->premap (pref_addr, maplength, &state->cookie);
+	  if (addr != MAP_FAILED)
+	    return addr;
+	}
+
+      afct = afct->next;
+   }
+   return MAP_FAILED;
+}
+
 void
 _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
 {
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 89c0833544..cb630720d5 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -90,6 +90,7 @@ _dl_map_segments (struct link_map *l, void *fd,
 
   if (__glibc_likely (type == ET_DYN))
     {
+      bool fixed_addr = false;
       /* This is a position-independent shared object.  We can let the
          kernel map it anywhere it likes, but we must have space for all
          the segments in their specified positions relative to the first.
@@ -105,10 +106,23 @@ _dl_map_segments (struct link_map *l, void *fd,
         = (ELF_PREFERRED_ADDRESS (loader, maplength, c->mapstart)
            - MAP_BASE_ADDR (l));
 
-      /* Remember which part of the address space this object uses.  */
-      l->l_map_start = _dl_map_segment (c, mappref, maplength);
-      if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
-        return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
+#ifdef SHARED
+      /* Call audit to let it change the address if he wants. */
+      void *pref_addr = _dl_audit_premap (l, (void *) mappref, maplength);
+      if (pref_addr != MAP_FAILED)
+        {
+          l->l_map_start = (ElfW(Addr)) pref_addr;
+          fixed_addr = true;
+        }
+#endif
+
+      if (!fixed_addr)
+        {
+          /* Remember which part of the address space this object uses.  */
+          l->l_map_start = _dl_map_segment (c, mappref, maplength);
+          if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
+            return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
+        }
 
       l->l_map_end = l->l_map_start + maplength;
       l->l_addr = l->l_map_start - c->mapstart;
diff --git a/elf/link.h b/elf/link.h
index b59038c680..462df5b0bf 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -194,6 +194,8 @@ extern void la_activity (uintptr_t *__cookie, unsigned int __flag);
 extern char *la_objsearch (const char *__name, uintptr_t *__cookie,
 			   unsigned int __flag);
 extern int la_premap_dlmem (size_t maplength, uintptr_t *__cookie);
+extern void *la_premap (void *pref_addr, size_t maplength,
+			uintptr_t *__cookie);
 extern unsigned int la_objopen (struct link_map *__map, Lmid_t __lmid,
 				uintptr_t *__cookie);
 extern void la_preinit (uintptr_t *__cookie);
diff --git a/elf/rtld.c b/elf/rtld.c
index ce35d0a28b..b63937c5b9 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -990,7 +990,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
       return;
     }
 
-  enum { naudit_ifaces = 9 };
+  enum { naudit_ifaces = 10 };
   union
   {
     struct audit_ifaces ifaces;
@@ -1005,6 +1005,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
     "la_activity\0"
     "la_objsearch\0"
     "la_premap_dlmem\0"
+    "la_premap\0"
     "la_objopen\0"
     "la_preinit\0"
     LA_SYMBIND "\0"
diff --git a/elf/tst-audit-dlmem.c b/elf/tst-audit-dlmem.c
index 87c6d3741e..18ae13bd68 100644
--- a/elf/tst-audit-dlmem.c
+++ b/elf/tst-audit-dlmem.c
@@ -80,10 +80,24 @@ static int
 handle_restart (void)
 {
   {
+    Dl_info info;
     void *h = dlmem_wrapper ("../" LIBC_SO, RTLD_NOW);
 
     pid_t (*s) (void) = xdlsym (h, "getpid");
     TEST_COMPARE (s (), getpid ());
+    if (!dladdr (s, &info))
+      {
+        printf ("dladdr failed\n");
+        exit (EXIT_FAILURE);
+      }
+#ifdef MAP_32BIT
+    /* Our test audit module maps everything to lower 32bit. */
+    if ((unsigned long)info.dli_fbase >= 0x100000000)
+      {
+        printf ("premap audit didn't work\n");
+        exit (EXIT_FAILURE);
+      }
+#endif
 
     xdlclose (h);
   }
@@ -103,6 +117,14 @@ handle_restart (void)
         printf ("dladdr failed\n");
         exit (EXIT_FAILURE);
       }
+#ifdef MAP_32BIT
+    /* Our test audit module maps everything to lower 32bit. */
+    if ((unsigned long)info.dli_fbase >= 0x100000000)
+      {
+        printf ("premap audit didn't work\n");
+        exit (EXIT_FAILURE);
+      }
+#endif
     fprintf (stderr, "offset bar %lx\n", info.dli_saddr - info.dli_fbase);
     /* write another value for parent to read */
     *bar = TEST_BAR_VAL;
@@ -148,6 +170,7 @@ do_test (int argc, char *argv[])
     { "la_objsearch", false },
     { "la_activity", false },
     { "la_premap_dlmem", false },
+    { "la_premap", false },
     { "la_objopen", false },
     { "la_objclose", false },
     { "la_preinit", false },
diff --git a/elf/tst-audit18.c b/elf/tst-audit18.c
index 5d13dd0a48..f7ac9d45c3 100644
--- a/elf/tst-audit18.c
+++ b/elf/tst-audit18.c
@@ -91,6 +91,7 @@ do_test (int argc, char *argv[])
     { "la_version", false },
     { "la_objsearch", false },
     { "la_activity", false },
+    { "la_premap", false },
     { "la_objopen", false },
     { "la_objclose", false },
     { "la_preinit", false },
diff --git a/elf/tst-auditmod-dlmem.c b/elf/tst-auditmod-dlmem.c
index bd413da436..2486bd8882 100644
--- a/elf/tst-auditmod-dlmem.c
+++ b/elf/tst-auditmod-dlmem.c
@@ -68,6 +68,22 @@ la_premap_dlmem (size_t maplength, uintptr_t *cookie)
   return fd;
 }
 
+void *
+la_premap (void *addr, size_t maplength, uintptr_t *cookie)
+{
+  void *ret = MAP_FAILED;
+  fprintf (stderr, "%s\n", __func__);
+  /* If addr already set by elf, don't change. */
+  if (addr)
+    return MAP_FAILED;
+#ifdef MAP_32BIT
+  /* This mapping will be overwritten so use PROT_NONE. */
+  ret = mmap(NULL, maplength, PROT_NONE,
+             MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
+#endif
+  return ret;
+}
+
 unsigned int
 la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
 {
diff --git a/elf/tst-auditmod18.c b/elf/tst-auditmod18.c
index cacada8d95..c16e28e8cb 100644
--- a/elf/tst-auditmod18.c
+++ b/elf/tst-auditmod18.c
@@ -18,6 +18,7 @@
 
 #include <stdio.h>
 #include <link.h>
+#include <sys/mman.h>  // for MAP_FAILED
 
 unsigned int
 la_version (unsigned int version)
@@ -39,6 +40,13 @@ la_activity (uintptr_t *cookie, unsigned int flag)
   fprintf (stderr, "%s\n", __func__);
 }
 
+void *
+la_premap (void *addr, size_t mapsize, uintptr_t *cookie)
+{
+  fprintf (stderr, "%s\n", __func__);
+  return MAP_FAILED;
+}
+
 unsigned int
 la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
 {
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 26000e1e81..8d8ee00b95 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -240,6 +240,7 @@ struct audit_ifaces
   void (*activity) (uintptr_t *, unsigned int);
   char *(*objsearch) (const char *, uintptr_t *, unsigned int);
   int (*premap_dlmem) (size_t, uintptr_t *);
+  void *(*premap) (void *, size_t, uintptr_t *);
   unsigned int (*objopen) (struct link_map *, Lmid_t, uintptr_t *);
   void (*preinit) (uintptr_t *);
   union
@@ -1373,6 +1374,11 @@ void _dl_audit_activity_nsid (Lmid_t nsid, int action)
 int _dl_audit_premap_dlmem (struct link_map *l, size_t maplength)
   attribute_hidden;
 
+/* Call la_premap from the audit modules from the link map and
+   get back an address to which the solib will be mapped.  */
+void *_dl_audit_premap (struct link_map *l, void *pref_addr, size_t maplength)
+  attribute_hidden;
+
 /* Call the la_objopen from the audit modules for the link_map L on the
    namespace identification NSID.  */
 void _dl_audit_objopen (struct link_map *l, Lmid_t nsid)
-- 
2.37.2


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

* Re: [PATCH 2/3] dlfcn,elf: implement dlmem() and audit [BZ #11767]
  2023-02-15 16:55 ` [PATCH 2/3] dlfcn,elf: implement dlmem() and audit [BZ #11767] Stas Sergeev
@ 2023-02-20 15:50   ` Carlos O'Donell
  2023-02-20 16:35     ` stsp
  2023-03-18 17:04     ` stsp
  0 siblings, 2 replies; 7+ messages in thread
From: Carlos O'Donell @ 2023-02-20 15:50 UTC (permalink / raw)
  To: Stas Sergeev, libc-alpha, Paul Pluzhnikov

On 2/15/23 11:55, Stas Sergeev via Libc-alpha wrote:
> This patch adds the following function:
> void *dlmem(const unsigned char *buffer, size_t size, int flags);

Stas,

Thank you very much for this contribution. This patch series was raised during the
weekly patch review and I wanted to take the time review before your implementation
moves too far forward.

My understanding of your use case is based on the discussion you had in bug 30007,
and I also reviewed bug 11767 for background. I also reviewed the discussions on
libc-alpha from Joseph Myers, Florian Weimer, and Andreas Schwab. Florian has open
questions about gdb and audit requirements which I didn't see addressed in this
series.

tl;dr

My position is that the semantics of dlmem() is at a level of abstraction that
impacts the present security, audit, and developer requirements, and that something
like BSDs fdlopen() or Google's dlopen_with_offset() would serve as a better
solution overall.

Details
=======

There are several paths forward for the uses cases discussed, but two of them
come to mind and have seen some discussion on this list:

(a) fdlopen() as in BSD.

* All normal operations that dlopen would do are done, but starting from an fd.

* Mirrors other new APIs e.g. signalfd, pidfd etc. Maybe we call dlopenfd() on Linux.

(b) dlmem() as in your patch.

The semantic issues I see with dlmem() are as follows:

* No guarantee that the memory that is loaded meets the preconditions for the
  dynamic loader and application uses.

  - We could argue that it is the application that needs to meet those requirements
    or the system is in an undefined state, however the better an API is the more
    it ensures by design that we cannot be in such states.

* The API pushes the abstraction of a "file descriptor" completely out of the design
  and in doing so introduces a level of change that requires a new LD_AUDIT interface,
  and likely other tooling changes.

If instead the semantics are raised up a level to fdlopen() then we have:

* Application developer can still do everything they wanted, but they need one
  additional syscall, memfd_create() to turn the memory into a file descriptor
  and that has the added benefit of being a kernel-side auditable event which is
  already used in JIT/FFI e.g. libffi.

  - Kernel support appeared in Linux 3.17 and glibc 2.27 (2018).

  - Kernel lockdown of memfd_create() is a known issue and discussed by JITs
    with kernel developers e.g. OpenJDK uses memfd_create(), libffi uses
    memfd_create().

* Additionally users with the use case that they have an embedded DSO on disk
  now need to do less work in their application because they can use fdlopen()
  with an fd already open and at a suitable offset. Note that the fd passed is
  never closed.

* No new LD_AUDIT interfaces reuqired so auditors and developer tooling does
  not need to be updated.

  - Though now it's possible to skip la_objsearch for the opening of the object,
    but that was always a possible scenario.

As of today I  Google has dlopen_with_offset(), almost fdlopen(),
in google/grte/v5-2.27/master, but it has not been contributed for general
inclusion. This to me seems to indicate that industry best practice today
continues to be around the use of a file descriptor.

My suggestion therefore is to attempt to refactor what you have around an API
that is like fdlopen(), and see what the implementation and performance looks
like in glibc.

Thoughts?

> It is the same as dlopen() but allows to dynamic-link solibs from
> the memory buffer, rather than from a file as dlopen() does.
> 
> "buffer" arg is the pointer to the solib image in memory.
> "size" is the solib image size. Must be smaller-or-equal to the
> actual buffer size.
> "flags" is the same flags argument used in dlopen().
> 
> The idea behind the implementation is very simple: where the
> dlopen() would mmap() the file, dlmem() does anonymous
> mmap()+memcpy().
> Unfortunately the glibc code was too bound to the file reads,
> so the patch looks bigger than it should. Some refactorings
> were needed to avoid big copy/pasts and code duplications.
> In particular, _dl_map_object_from_fd() was split and now calls
> 2 functions that are also called from __dl_map_object_from_mem().
> The same treatment was applied to open_verify() - the part that
> can be shared, was split into do_open_verify().
> 
> This patch adds a test-case named tst-dlmem.
> 
> This patch also adds the _dl_audit_premap_dlmem LD_AUDIT
> extension. It passes map length to la_premap_dlmem() and gets
> back an fd. If returned fd==-1 then the dlmem() space is
> anonymously mapped, which is a default behavior w/o audit.
> But it is possible to create a shm area of the needed size
> with shm_open()/ftruncate() and return that fd. Then you
> will be able to access the solib image via that fd, so that
> you can mmap() it to another process or another address of
> the same process.
> 
> The test-case for that functionality is called tst-audit-dlmem.
> 
> The test-suite was run on x86_64/64 and showed no regressions.
> 
> Signed-off-by: Stas Sergeev <stsp2@yandex.ru>



-- 
Cheers,
Carlos.


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

* Re: [PATCH 2/3] dlfcn,elf: implement dlmem() and audit [BZ #11767]
  2023-02-20 15:50   ` Carlos O'Donell
@ 2023-02-20 16:35     ` stsp
  2023-03-18 17:04     ` stsp
  1 sibling, 0 replies; 7+ messages in thread
From: stsp @ 2023-02-20 16:35 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha, Paul Pluzhnikov

Hi Carlos, thanks for a feed-back!

20.02.2023 20:50, Carlos O'Donell пишет:
> My understanding of your use case is based on the discussion you had in bug 30007,
> and I also reviewed bug 11767 for background. I also reviewed the discussions on
> libc-alpha from Joseph Myers, Florian Weimer, and Andreas Schwab. Florian has open
> questions about gdb and audit requirements which I didn't see addressed in this
> series.

I probably misunderstood Florian if
he meant I need to address something
particular. The problem I see with gdb
is that it doesn't recognize such library
load and therefore doesn't autoload the
debug info from solib. Manual debuginfo
loading might be a temporary solution.
If something else was meant and I need
to do some changes to the patch, please
explain to me once again, what is it.

Overall, if there are some requested changes
pending, please explain them to me
again, as currently I am not aware of any.


> My position is that the semantics of dlmem() is at a level of abstraction that
> impacts the present security, audit, and developer requirements, and that something
> like BSDs fdlopen() or Google's dlopen_with_offset() would serve as a better
> solution overall.
>
> Details
> =======
>
> There are several paths forward for the uses cases discussed, but two of them
> come to mind and have seen some discussion on this list:

Let me first assert (and explain down below)
that none of them brings in the functionality
that I need and have in dlmem().


> (a) fdlopen() as in BSD.
>
> * All normal operations that dlopen would do are done, but starting from an fd.
>
> * Mirrors other new APIs e.g. signalfd, pidfd etc. Maybe we call dlopenfd() on Linux.

fdlopen() is a very good API.
But it serves another purposes and gives
nothing for me.

> (b) dlmem() as in your patch.
>
> The semantic issues I see with dlmem() are as follows:
>
> * No guarantee that the memory that is loaded meets the preconditions for the
>    dynamic loader and application uses.

Not sure what you mean.
dlmem() works very simply: you give it
the buffer with file image and it gives you
the buffer with relocated solib image.
What preconditions?


> * The API pushes the abstraction of a "file descriptor" completely out of the design
>    and in doing so introduces a level of change that requires a new LD_AUDIT interface,
>    and likely other tooling changes.

No, let me clarify.
The patch has 2 new audit interfaces:
- premap_dlmem - it allows to feed in the
   backing-store fd for solib image, making
   dlmem() unique. You simply can't, by definition,
   make something like that on any API with fd.

- premap - this is a generic audit interface that
   can as well be used with dlopen(). It allows
   to specify the relocation address for PIC solib.

So no, nothing of the above is _required_ for
dlmem() to work. But one extends it in a way
you can't do with any competing API, and another
one is a generic extension because I need to
be able to set the relocation address for PIC solibs,
no matter from what media they are loaded.

Here I feel that the design is not properly understood,
and maybe I need to address that by doing some
write-up? Or is the above short explanation sufficient?

Note that I provided examples for both extensions.
There is an example that feeds a backing-store fd
and maps solib to 2 processes. It then makes sure
that when you change the var in 1 process, that var
is changed in another process. How would you do
the same with any competing API? Impossible.

Another example shows that you can use MAP_32BIT
flag to relocate the solib into a first 4Gb space.
This is a generic extension, works with dlopen()
too, and of course can work with fdlopen() or
whatever, but the first extension - backing-store
fd - is unique for dlmem(). Which is why I need
dlmem(). :)


> If instead the semantics are raised up a level to fdlopen() then we have:
>
> * Application developer can still do everything they wanted,

No, no! And that's the point.
How would you re-create my solib-sharing
example with anything but my dlmem() impl?


>   but they need one
>    additional syscall, memfd_create() to turn the memory into a file descriptor
>    and that has the added benefit of being a kernel-side auditable event which is
>    already used in JIT/FFI e.g. libffi.
>
>    - Kernel support appeared in Linux 3.17 and glibc 2.27 (2018).

I am not even sure why memfd_create()
is always mentioned. I used shm_open()
for such things, and I can even dlopen()
from shm object.


> * Additionally users with the use case that they have an embedded DSO on disk
>    now need to do less work in their application because they can use fdlopen()
>    with an fd already open and at a suitable offset. Note that the fd passed is
>    never closed.

I am all for fdlopen()!
Its good API. Please implement it. :)
But its different, and in that particular
case I don't need it and couldn't use it
even if it is there.


> * No new LD_AUDIT interfaces reuqired so auditors and developer tooling does
>    not need to be updated.

They are not _required_.
But they add 2 valuable extensions.
And in fact, the dlmem() itself is out of
any interest w/o those 2 extensions.
So I need these extensions, and dlmem()
is what makes them (one of them) possible.
Second extension is generic, but I need both
so I see no way around dlmem().


>    - Though now it's possible to skip la_objsearch for the opening of the object,
>      but that was always a possible scenario.
>
> As of today I  Google has dlopen_with_offset(), almost fdlopen(),
> in google/grte/v5-2.27/master, but it has not been contributed for general
> inclusion. This to me seems to indicate that industry best practice today
> continues to be around the use of a file descriptor.
>
> My suggestion therefore is to attempt to refactor what you have around an API
> that is like fdlopen(), and see what the implementation and performance looks
> like in glibc.
>
> Thoughts?
Only if you can show how to relocate
the solib into the shared buffer. Which
is IMO not possible w/o dlmem().

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

* Re: [PATCH 2/3] dlfcn,elf: implement dlmem() and audit [BZ #11767]
  2023-02-20 15:50   ` Carlos O'Donell
  2023-02-20 16:35     ` stsp
@ 2023-03-18 17:04     ` stsp
  1 sibling, 0 replies; 7+ messages in thread
From: stsp @ 2023-03-18 17:04 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha, Paul Pluzhnikov

Hi Carlos,

20.02.2023 20:50, Carlos O'Donell пишет:
> * No new LD_AUDIT interfaces reuqired so auditors and developer tooling does
>    not need to be updated.

I dropped LD_AUDIT stuff just as you say.


> My suggestion therefore is to attempt to refactor what you have around an API
> that is like fdlopen(), and see what the implementation and performance looks
> like in glibc.
>
> Thoughts?
Done in a just posted v9.
It has fdlopen() in tst-dlmem-fdlopen.
Its source are also listed below.
I think that impl should go to libbsd,
not into glibc though.
What do you think?

Source:

static void *
fdlopen (int fd, int flags)
{
   off_t len;
   void *addr;
   void *handle;

   len = lseek (fd, 0, SEEK_END);
   lseek (fd, 0, SEEK_SET);
   addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
   if (addr == MAP_FAILED)
     {
       printf ("cannot mmap, %s\n", strerror(errno));
       exit (EXIT_FAILURE);
     }
   handle = dlmem (addr, len, flags, NULL);
   munmap (addr, len);
   return handle;
}


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

end of thread, other threads:[~2023-03-18 17:04 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-15 16:55 [PATCH v7 0/3] implement dlmem() with audit extensions Stas Sergeev
2023-02-15 16:55 ` [PATCH 1/3] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
2023-02-15 16:55 ` [PATCH 2/3] dlfcn,elf: implement dlmem() and audit [BZ #11767] Stas Sergeev
2023-02-20 15:50   ` Carlos O'Donell
2023-02-20 16:35     ` stsp
2023-03-18 17:04     ` stsp
2023-02-15 16:55 ` [PATCH 3/3] elf/dl-audit: add _dl_audit_premap fn [BZ #30007] 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).