[12/13] dlfcn,elf: implement dlmem() [BZ #11767]

Message ID 20230318165110.3672749-13-stsp2@yandex.ru
State Superseded
Headers
Series implement dlmem() function |

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

stsp March 18, 2023, 4:51 p.m. UTC
  This patch adds the following function:
void *dlmem(const unsigned char *buffer, size_t size, int flags,
            struct dlmem_args *dlm_args);

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().
"dlm_args" is an optional argument that allows to specify the load
    namespace and a premap callback.

This implementation is trying a "zero-copy" technique first, but
it works only with linux kernels 5.13 and newer. So the memcpy()
fall-back is added as well.

This patch adds a test-case named tst-dlmem-fdlopen. It implements
a bsd-compatible fdlopen() on top of dlmem() and loads a test lib
with it. It then checks /proc/<pid>/maps to make sure the library
was mmap()ed rather then memcopied. Then it does the regular set
of solib tests.

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/dlfcn.h                                 |  22 +++
 dlfcn/dlmem.c                                 | 102 +++++++++++
 dlfcn/tst-dlmem-fdlopen.c                     | 106 +++++++++++
 elf/dl-load.c                                 | 169 ++++++++++++++++++
 elf/dl-load.h                                 |   3 +
 elf/dl-main.h                                 |  12 ++
 elf/dl-open.c                                 |  13 ++
 elf/rtld.c                                    |   1 +
 include/dlfcn.h                               |   4 +
 manual/dynlink.texi                           |   1 +
 sysdeps/generic/ldsodefs.h                    |   9 +
 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 +
 48 files changed, 484 insertions(+), 1 deletion(-)
 create mode 100644 dlfcn/dlmem.c
 create mode 100644 dlfcn/tst-dlmem-fdlopen.c
  

Comments

Carlos O'Donell March 29, 2023, 1:45 p.m. UTC | #1
On 3/18/23 12:51, Stas Sergeev via Libc-alpha wrote:
> This patch adds the following function:
> void *dlmem(const unsigned char *buffer, size_t size, int flags,
>             struct dlmem_args *dlm_args);

I am raising a sustained objection to including dlmem() in glibc.

I appreciate your effort in working on this serious, and I think *many* of
the core changes you propose are good cleanups.

In my experience it is the wrong level of abstraction.

To implement fdlopen on top of dlmem requires PT_LOAD processing and that
will duplicate into userspace a significant part of the complexity of ELF
loading and segment handling. The test case you include below is incomplete
when it comes to implementing a functional fdlopen() across the supported
architectures and toolchain variants.

When an interface requires duplication of complex load segment handling
into a user program it means that we have the abstraction wrong.

You could add an dlload() interface which reads everything from disk, and then
dlmem() to process it, but then why not just make it fdlopen()?

You have previously stated that none of them bring the functionality you need
from dlmem() and that might be true, but glibc is not here to serve individual
purposes, it is here to serve the requirements of the larger ecosystem of
applications and their most standardized requirements. Very specific functionality,
like futexes for example, we don't even export, because they are difficult to use
without a wrapping library that supports their use, similar to io_uring, and other
asynchronous IO mechanisms.

If you strongly believe that dlmem() serves broader applications, then I suggest
looking at previous examples of smaller features entering glibc.

There we tended to do:

- Write up a detailed design document e.g. 
  https://sourceware.org/glibc/wiki/Y2038ProofnessDesign
  https://sourceware.org/glibc/wiki/Proposals/GroupMerging

- Land all cleanups required first.
  - Your cleanups may be useful even outside of the existing design.
  - Auditor additions may be landed independent of the design.
 
- Discuss the blockers, one by one, and attempt to get consensus.
  https://sourceware.org/glibc/wiki/Consensus#How_do_I_build_consensus.3F

Today you have a blocker from me about the level of abstraction of the interface.
You need to convince me that this interface is:

(a) Useful for many applications.

(b) Does not require complex logic on the application part.
    - or -
    Supports the application by providing another API to do loading.
 
> 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().
> "dlm_args" is an optional argument that allows to specify the load
>     namespace and a premap callback.
> 
> This implementation is trying a "zero-copy" technique first, but
> it works only with linux kernels 5.13 and newer. So the memcpy()
> fall-back is added as well.
> 
> This patch adds a test-case named tst-dlmem-fdlopen. It implements
> a bsd-compatible fdlopen() on top of dlmem() and loads a test lib
> with it. It then checks /proc/<pid>/maps to make sure the library
> was mmap()ed rather then memcopied. Then it does the regular set
> of solib tests.
> 
> 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/dlfcn.h                                 |  22 +++
>  dlfcn/dlmem.c                                 | 102 +++++++++++
>  dlfcn/tst-dlmem-fdlopen.c                     | 106 +++++++++++
>  elf/dl-load.c                                 | 169 ++++++++++++++++++
>  elf/dl-load.h                                 |   3 +
>  elf/dl-main.h                                 |  12 ++
>  elf/dl-open.c                                 |  13 ++
>  elf/rtld.c                                    |   1 +
>  include/dlfcn.h                               |   4 +
>  manual/dynlink.texi                           |   1 +
>  sysdeps/generic/ldsodefs.h                    |   9 +
>  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 +
>  48 files changed, 484 insertions(+), 1 deletion(-)
>  create mode 100644 dlfcn/dlmem.c
>  create mode 100644 dlfcn/tst-dlmem-fdlopen.c
> 
> diff --git a/dlfcn/Makefile b/dlfcn/Makefile
> index 1fa7fea1ef..8bca644168 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-fdlopen
> +CPPFLAGS-tst-dlmem-fdlopen.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-fdlopen.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/dlfcn.h b/dlfcn/dlfcn.h
> index c5d192597d..87dc4932fd 100644
> --- a/dlfcn/dlfcn.h
> +++ b/dlfcn/dlfcn.h
> @@ -68,6 +68,28 @@ extern void *dlsym (void *__restrict __handle,
>  /* Like `dlopen', but request object to be allocated in a new namespace.  */
>  extern void *dlmopen (Lmid_t __nsid, const char *__file, int __mode) __THROWNL;
>  
> +/* Callback for dlmem. */
> +typedef void *
> +(dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
> +	          void *cookie);
> +
> +struct dlmem_args {
> +  /* Optional name to associate with the loaded object. */
> +  const char *soname;
> +  /* Namespace where to load the object. */
> +  Lmid_t nsid;
> +  /* dlmem-specific flags. */
> +  unsigned flags;
> +  /* Optional premap callback. */
> +  dlmem_premap_t *premap;
> +  /* Optional argument for premap callback. */
> +  void *cookie;
> +};
> +
> +/* Like `dlmopen', but loads shared object from memory buffer.  */
> +extern void *dlmem (const unsigned char *buffer, size_t size, int mode,
> +		    struct dlmem_args *dlm_args);
> +
>  /* Find the run-time address in the shared object HANDLE refers to
>     of the symbol called NAME with VERSION.  */
>  extern void *dlvsym (void *__restrict __handle,
> diff --git a/dlfcn/dlmem.c b/dlfcn/dlmem.c
> new file mode 100644
> index 0000000000..548f6f34eb
> --- /dev/null
> +++ b/dlfcn/dlmem.c
> @@ -0,0 +1,102 @@
> +/* 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;
> +  struct dlmem_args *args;
> +  /* The return value of dlmem_doit.  */
> +  void *new;
> +  /* Address of the caller.  */
> +  const void *caller;
> +};
> +
> +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->args,
> +			    args->caller,
> +			    __libc_argc, __libc_argv, __environ);
> +}
> +
> +
> +static void *
> +dlmem_implementation (const unsigned char *buffer, size_t size, int mode,
> +		      struct dlmem_args *dlm_args, void *dl_caller)
> +{
> +  struct _dlmem_args args;
> +  args.buffer = buffer;
> +  args.size = size;
> +  args.mode = mode;
> +  args.args = dlm_args;
> +  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,
> +	  struct dlmem_args *dlm_args)
> +{
> +  if (GLRO (dl_dlfcn_hook) != NULL)
> +    return GLRO (dl_dlfcn_hook)->dlmem (buffer, size, mode, dlm_args,
> +                                        RETURN_ADDRESS (0));
> +  else
> +    return dlmem_implementation (buffer, size, mode, dlm_args,
> +				 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,
> +	 struct dlmem_args *dlm_args, void *dl_caller)
> +{
> +  return dlmem_implementation (buffer, size, mode, dlm_args, dl_caller);
> +}
> +
> +void *
> +___dlmem (const unsigned char *buffer, size_t size, int mode,
> +	  struct dlmem_args *dlm_args)
> +{
> +  return __dlmem (buffer, size, mode, dlm_args, RETURN_ADDRESS (0));
> +}
> +weak_alias (___dlmem, dlmem)
> +static_link_warning (dlmem)
> +#endif /* !SHARED */
> diff --git a/dlfcn/tst-dlmem-fdlopen.c b/dlfcn/tst-dlmem-fdlopen.c
> new file mode 100644
> index 0000000000..db9b5cf54a
> --- /dev/null
> +++ b/dlfcn/tst-dlmem-fdlopen.c
> @@ -0,0 +1,106 @@
> +/* Test for fdlopen implementation on top of 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>
> +#include <support/check.h>
> +
> +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);

This may not meet the requirements of the file being opened.

Each PT_LOAD segment must be mapped according to the requiremenst of the
segment.


> +  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;
> +}
> +
> +
> +#define TEST_FUNCTION do_test
> +extern int do_test (void);
> +
> +int
> +do_test (void)
> +{
> +  char cmd[256];
> +  void *handle;
> +  int (*sym) (void); /* We load ref1 from glreflib1.c.  */
> +  Dl_info info;
> +  int rc;
> +  int fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
> +  if (fd == -1)
> +    error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
> +  handle = fdlopen (fd, RTLD_NOW);
> +  close (fd);
> +  if (handle == NULL)
> +    {
> +      printf ("fdlopen failed, %s\n", dlerror());
> +      exit (EXIT_FAILURE);
> +    }
> +
> +  /* Check that the lib is properly mmap()ed, rather than memcpy()ed.
> +     This may fail on linux kernels <5.13. */
> +  snprintf (cmd, sizeof(cmd), "grep glreflib1.so /proc/%i/maps", getpid());
> +  rc = system (cmd);
> +  TEST_COMPARE (rc, 0);
> +
> +  sym = dlsym (handle, "ref1");
> +  if (sym == NULL)
> +    error (EXIT_FAILURE, 0, "dlsym failed");
> +
> +  memset (&info, 0, sizeof (info));
> +  rc = dladdr (sym, &info);
> +  if (rc == 0)
> +    error (EXIT_FAILURE, 0, "dladdr failed");
> +
> +  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);
> +
> +  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");
> +  return 0;
> +}
> +
> +
> +#include <support/test-driver.c>
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 7175b99962..2550173f48 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -75,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>
> @@ -2353,6 +2354,174 @@ _dl_map_object (struct link_map *loader, const char *name,
>    return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid);
>  }
>  
> +static void *
> +do_memremap (void *addr, size_t length, int prot, int flags,
> +             void *arg, off_t offset)
> +{
> +  const struct dlmem_fbuf *fb = arg;
> +
> +  assert (flags & MAP_FIXED);
> +  if (offset < fb->len)
> +    {
> +      size_t to_copy = length;
> +      if (offset + to_copy > fb->len)
> +        to_copy = fb->len - offset;
> +#ifdef MREMAP_DONTUNMAP
> +      void *addr2 = __mremap ((void *) (fb->buf + offset), to_copy, to_copy,
> +                              MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_DONTUNMAP,
> +                              addr);
> +      /* MREMAP_DONTUNMAP introduced in linux-5.7, but only works for
> +         file-based maps since commit a460938 went in 5.13.
> +         So have a fall-back. */
> +      if (addr2 == MAP_FAILED)
> +        memcpy (addr, fb->buf + offset, to_copy);
> +#else
> +      /* MREMAP_DONTUNMAP is not always available. This is a fall-back. */
> +      memcpy (addr, fb->buf + offset, to_copy);
> +#endif
> +    }
> +  if (__mprotect (addr, length, prot) == -1)
> +    return MAP_FAILED;
> +  return addr;
> +}
> +
> +static void *
> +do_dlmem_premap (void *mappref, size_t maplength, size_t mapalign,
> +		 void *cookie)
> +{
> +  struct dlmem_fbuf *fb = cookie;
> +  void *ret = MAP_FAILED;
> +
> +  if (fb->dlm_args && fb->dlm_args->premap)
> +    ret = fb->dlm_args->premap (mappref, maplength, mapalign,
> +                                fb->dlm_args->cookie);
> +  if (ret == MAP_FAILED)
> +    ret = (void *) _dl_map_segment ((ElfW(Addr)) mappref, maplength,
> +                                    mapalign);
> +  return ret;
> +}
> +
> +static ssize_t
> +do_pread_memcpy (void *arg, void *buf, size_t count, off_t offset)
> +{
> +  struct dlmem_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 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;
> +  char *realname;
> +  /* 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));
> +
> +  if (name && *name)
> +    {
> +      /* Look for this name among those already loaded.  */
> +      l = _dl_check_loaded (name, nsid);
> +      if (l)
> +        return l;
> +    }
> +
> +  /* 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);
> +
> +  /* _dl_new_object() treats "" separately and doesn't free it. */
> +  realname = *name ? __strdup (name) : (char *) "";
> +  /* Enter the new object in the list of loaded objects.  */
> +  l = _dl_new_object (realname, 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_memremap, 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 e777da5838..09b2878260 100644
> --- a/elf/dl-load.h
> +++ b/elf/dl-load.h
> @@ -106,6 +106,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);
>  
>  /* 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,
> diff --git a/elf/dl-main.h b/elf/dl-main.h
> index 344a87d5e8..e60fafaeb6 100644
> --- a/elf/dl-main.h
> +++ b/elf/dl-main.h
> @@ -104,6 +104,13 @@ struct dl_main_state
>    bool version_info;
>  };
>  
> +struct dlmem_fbuf
> +{
> +  ssize_t len;
> +  const unsigned char *buf;
> +  struct dlmem_args *dlm_args;
> +};
> +
>  /* 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.  */
> @@ -112,6 +119,11 @@ __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.  */
> diff --git a/elf/dl-open.c b/elf/dl-open.c
> index f3886c21bc..7c7bb2ef4f 100644
> --- a/elf/dl-open.c
> +++ b/elf/dl-open.c
> @@ -953,6 +953,19 @@ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
>                       __dl_map_object);
>  }
>  
> +void *
> +_dl_mem (const unsigned char *buffer, size_t size, int mode,
> +	 struct dlmem_args *dlm_args, const void *caller_dlopen,
> +	 int argc, char *argv[], char *env[])
> +{
> +  struct dlmem_fbuf fb = { .buf = buffer, .len = size, .dlm_args = dlm_args };
> +  Lmid_t nsid = dlm_args ? dlm_args->nsid : LM_ID_BASE;
> +  const char *file = (dlm_args && dlm_args->soname) ? dlm_args->soname : "";
> +
> +  return do_dl_open (file, &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/rtld.c b/elf/rtld.c
> index f82fbeb132..1877ec9f16 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,
> diff --git a/include/dlfcn.h b/include/dlfcn.h
> index ae25f05303..6205cf407d 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,
> +		  struct dlmem_args *dlm_args, 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,
> +		      struct dlmem_args *dlm_args, 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..abca95b18c 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -669,6 +669,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,
> +		    struct dlmem_args *dlm_args, const void *caller_dlopen,
> +		    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
> @@ -1249,6 +1252,12 @@ extern void *_dl_open (const char *name, int mode, const void *caller,
>  		       Lmid_t nsid, int argc, char *argv[], char *env[])
>       attribute_hidden;
>  
> +/* Open shared object from memory buffer. */
> +extern void *_dl_mem (const unsigned char *buffer, size_t size, int mode,
> +		      struct dlmem_args *dlm_args, const void *caller,
> +		      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
>     old scope, OLD can't be freed until no thread is using it.  */
> diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
> index ed0c4789eb..1880004336 100644
> --- a/sysdeps/mach/hurd/i386/libc.abilist
> +++ b/sysdeps/mach/hurd/i386/libc.abilist
> @@ -2326,6 +2326,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 0e2d9c3045..c4d31cabed 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2665,3 +2665,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 f1bec1978d..93b37a1e1d 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2774,6 +2774,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf 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 aa874b88d0..9fec2308c3 100644
> --- a/sysdeps/unix/sysv/linux/arc/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
> @@ -2426,3 +2426,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 afbd57da6f..bd0f04e58a 100644
> --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> @@ -546,6 +546,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 e7364cd3fe..dccc49c8d6 100644
> --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> @@ -543,6 +543,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 913fa59215..0df8524ac0 100644
> --- a/sysdeps/unix/sysv/linux/csky/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
> @@ -2702,3 +2702,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 43af3a9811..fa4680e97a 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -2651,6 +2651,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 af72f8fab0..bfb1bde49d 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2835,6 +2835,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 48cbb0fa50..e53505abe0 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -2600,6 +2600,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 c15884bb0b..9f56cbdcab 100644
> --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> @@ -2186,3 +2186,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_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 3738db81df..d5443b1198 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -547,6 +547,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 ed13627752..640c8b8c4a 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -2778,6 +2778,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 8357738621..79b400efc6 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> @@ -2751,3 +2751,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 58c5da583d..38b4098950 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> @@ -2748,3 +2748,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 d3741945cd..9a4f909a25 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -2743,6 +2743,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 5319fdc204..c8b9f85fdb 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -2741,6 +2741,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 1743ea6eb9..0887b67394 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -2749,6 +2749,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 9b1f53c6ac..1c3a6f4bee 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -2651,6 +2651,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 ae1c6ca1b5..31b23859a4 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2790,3 +2790,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 a7c572c947..59f4aa7766 100644
> --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
> @@ -2172,3 +2172,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 074fa031a7..d715d0ae97 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -2817,6 +2817,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf 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 dfcb4bd2d5..3addcf3d17 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -2850,6 +2850,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf 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 63bbccf3f9..5365978277 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> @@ -2571,6 +2571,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf 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 ab85fd61ef..f0af576192 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> @@ -2885,3 +2885,4 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf 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 b716f5c763..941fd40ea8 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> @@ -2428,3 +2428,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 774e777b65..74f58439ad 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> @@ -2628,3 +2628,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 8625135c48..cf3a10aa76 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -2815,6 +2815,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf 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 d00c7eb262..2ad97f87b2 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -2608,6 +2608,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf 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 b63037241d..7c81b94953 100644
> --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> @@ -2658,6 +2658,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 d80055617d..0493d6b456 100644
> --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> @@ -2655,6 +2655,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 5be55c11d2..6fd09f6499 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -2810,6 +2810,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf 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 475fdaae15..24dbc4801f 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -2623,6 +2623,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 6cfb928bc8..522ca8e8aa 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -2574,6 +2574,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf 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 c735097172..42b170c805 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2680,3 +2680,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
  
stsp March 29, 2023, 1:51 p.m. UTC | #2
29.03.2023 18:45, Carlos O'Donell пишет:
> On 3/18/23 12:51, Stas Sergeev via Libc-alpha wrote:
>> This patch adds the following function:
>> void *dlmem(const unsigned char *buffer, size_t size, int flags,
>>              struct dlmem_args *dlm_args);
> I am raising a sustained objection to including dlmem() in glibc.
>
> I appreciate your effort in working on this serious, and I think *many* of
> the core changes you propose are good cleanups.
>
> In my experience it is the wrong level of abstraction.
>
> To implement fdlopen on top of dlmem requires PT_LOAD processing and that
> will duplicate into userspace a significant part of the complexity of ELF
> loading and segment handling. The test case you include below is incomplete
> when it comes to implementing a functional fdlopen() across the supported
> architectures and toolchain variants.

Carlos, please, be reasonable, I don't understand
what do you want from me. :)
Why my test-cases are incomplete?
Why some "complete" test-case needs an elf
parsing? Will this ever be demonstrated with
some example or anything?
I am really a bit tired of that permanently
recurring argument about some elf parsing.
I don't even know what are you talking about.

Where, just where have you seen it in my
patch? Or if its not there, why do you mention
it?
  
Jonathon Anderson March 29, 2023, 2:10 p.m. UTC | #3
On Wed, 2023-03-29 at 18:51 +0500, stsp via Libc-alpha wrote:
> ```
> 
> 29.03.2023 18:45, Carlos O'Donell пишет:
> On 3/18/23 12:51, Stas Sergeev via Libc-alpha wrote:
> This patch adds the following function:
> > > void *dlmem(const unsigned char *buffer, size_t size, int flags,
> > >              struct dlmem_args *dlm_args);
> 
> > I am raising a sustained objection to including dlmem() in glibc.
> > 
> > I appreciate your effort in working on this serious, and I think *many* of
> > the core changes you propose are good cleanups.
> > 
> > In my experience it is the wrong level of abstraction.
> > 
> > To implement fdlopen on top of dlmem requires PT_LOAD processing and that
> > will duplicate into userspace a significant part of the complexity of ELF
> > loading and segment handling. The test case you include below is incomplete
> > when it comes to implementing a functional fdlopen() across the supported
> > architectures and toolchain variants.
> 
> 
> Carlos, please, be reasonable, I don't understand
> what do you want from me. :)
> Why my test-cases are incomplete?
> Why some "complete" test-case needs an elf
> parsing? Will this ever be demonstrated with
> some example or anything?
> I am really a bit tired of that permanently
> recurring argument about some elf parsing.
> I don't even know what are you talking about.
> 
> Where, just where have you seen it in my
> patch? Or if its not there, why do you mention
> it?

Stas,

Please do some research into the ELF file format. Neither your fdlopen implementation in the test cases nor your dlopen_with_offset implementation in the email chain implement it correctly.

AFAICT, the first glaring issue with both of your implementations is that you have neglected the case where p_offset != p_vaddr, i.e. a segment is mmapped to a different location than its layout in the file. There are a LOT of binaries out in the wild where this is the case. Here's a quick one-liner to help you find some on your own box, I have 11712 such binaries on my Debian system:

    find /usr/lib -type f -exec grep -aq '^.ELF' {} \; -print 2>/dev/null | while read bin; do if readelf -l $bin | grep LOAD | grep -vE 'LOAD[[:space:]]+(0x[0-9a-f]+) (\1)'; then echo $bin; fi; done

The second glaring issue (from my perspective) is that you are mmapping the entire file, instead of just the executable code. I have personally compiled up binaries where the DWARF debugging information was far larger than the code, one extreme case was a ~7.7GB binary of which merely ~130MB was .text. It is critical for performance that only that ~130MB is loaded from disk in the nominal case.

There are likely more glaring issues, these are just the two that first came to mind.

Needless to say, both of these issues require the user of dlmem() to parse the program headers of the binary and mmap() the binary according to the ELF standard. This is a complex and delicate process, and is already implemented in full in Glibc. The fact that users have to duplicate this implementation makes dlmem() a terrible API abstraction and, as others have asserted, unsuitable for inclusion Glibc.

-Jonathon
  
stsp March 29, 2023, 2:20 p.m. UTC | #4
29.03.2023 19:10, Jonathon Anderson пишет:
> Stas,
>
> Please do some research into the ELF file format. Neither your fdlopen implementation in the test cases nor your dlopen_with_offset implementation in the email chain implement it correctly.
>
> AFAICT, the first glaring issue with both of your implementations is that you have neglected the case where p_offset != p_vaddr, i.e. a segment is mmapped to a different location than its layout in the file. There are a LOT of binaries out in the wild where this is the case. Here's a quick one-liner to help you find some on your own box, I have 11712 such binaries on my Debian system:

Sure as hell p_offset != p_vaddr.
I never ever assumed it does!
OK, if it goes that badly, then I offer you
a deal.
If you present the solib with p_offset!=p_vaddr
and demonstrate that its broken with dlmem(),
and not because some random bug of mine but
exactly because p_offset!=p_vaddr, then I go
away from that dlmem() proposal forever.
If you can't, then you go away.
Do you accept that challenge?
Sorry for offering the silly stuff, but I simply
don't see how to proceed if we are wasting
the time on a things like that.
  
Adhemerval Zanella March 29, 2023, 2:31 p.m. UTC | #5
On 29/03/23 11:20, stsp via Libc-alpha wrote:
> 
> 29.03.2023 19:10, Jonathon Anderson пишет:
>> Stas,
>>
>> Please do some research into the ELF file format. Neither your fdlopen implementation in the test cases nor your dlopen_with_offset implementation in the email chain implement it correctly.
>>
>> AFAICT, the first glaring issue with both of your implementations is that you have neglected the case where p_offset != p_vaddr, i.e. a segment is mmapped to a different location than its layout in the file. There are a LOT of binaries out in the wild where this is the case. Here's a quick one-liner to help you find some on your own box, I have 11712 such binaries on my Debian system:
> 
> Sure as hell p_offset != p_vaddr.
> I never ever assumed it does!
> OK, if it goes that badly, then I offer you
> a deal.
> If you present the solib with p_offset!=p_vaddr
> and demonstrate that its broken with dlmem(),
> and not because some random bug of mine but
> exactly because p_offset!=p_vaddr, then I go
> away from that dlmem() proposal forever.
> If you can't, then you go away.
> Do you accept that challenge?
> Sorry for offering the silly stuff, but I simply
> don't see how to proceed if we are wasting
> the time on a things like that.
> 

This should be other way around since you are the one proposing a new
interface and thus should make sure that potential raised concerns 
indeed does not affect it. 

Also, I would like to ask to tune down your tone. This kind of aggressive
way to present technical topics, in a manner of adding 'challengers' to
put pressure on person that raised concerns is not best way and usually
make other developers to avoid engage further in the topic.
  
Carlos O'Donell March 29, 2023, 2:35 p.m. UTC | #6
On 3/29/23 10:20, stsp wrote:
> 
> 29.03.2023 19:10, Jonathon Anderson пишет:
>> Stas,
>>
>> Please do some research into the ELF file format. Neither your fdlopen implementation in the test cases nor your dlopen_with_offset implementation in the email chain implement it correctly.
>>
>> AFAICT, the first glaring issue with both of your implementations is that you have neglected the case where p_offset != p_vaddr, i.e. a segment is mmapped to a different location than its layout in the file. There are a LOT of binaries out in the wild where this is the case. Here's a quick one-liner to help you find some on your own box, I have 11712 such binaries on my Debian system:
> 
> Sure as hell p_offset != p_vaddr.
> I never ever assumed it does!
> OK, if it goes that badly, then I offer you
> a deal.

> If you present the solib with p_offset!=p_vaddr
> and demonstrate that its broken with dlmem(),
> and not because some random bug of mine but
> exactly because p_offset!=p_vaddr, then I go
> away from that dlmem() proposal forever.

The fact that such binaries can be created is enough for me to raise
a sustained objection to the inclusion of dlmem().

In glibc, and in ISO C, we deal in the slightly more abstract realm
of allowing developers to do as they wish within the boundaries of
the semantics we support. We do put some limits on what can be done
from a practical QoI perspective, but we try not be overly proscriptive.

> If you can't, then you go away.
> Do you accept that challenge?

Asking a developer to go away not the way consensus is built.

Jonathan is a scarce and precious resource, they are users that are
actively using LD_AUDIT interfaces for real work with hpctoolkit
and so are perfeclty positioned to provide us with useful feedback.

Please be kind to Jonathan.

> Sorry for offering the silly stuff, but I simply
> don't see how to proceed if we are wasting
> the time on a things like that.

If consensus can't be reached then the API will not be included
in glibc.

The real issue for me is that we are not providing a way for developers
to manage the complexity of the *setup* that is required before dlmem()
can operate reliably.

This is why I don't accept dlmem() as the right level of abstraciton.
It may solve you problem, but in glibc we need to do more than solve
singular problems, we need to provide interfaces that are useful for
many developers and the interfaces should be designed such that they
cannot be easily misused.
  
stsp March 29, 2023, 2:50 p.m. UTC | #7
29.03.2023 19:35, Carlos O'Donell пишет:
> The fact that such binaries can be created is enough for me to raise
> a sustained objection to the inclusion of dlmem().

Then someone needs to tell how exactly
it can be created, and, more importantly,
point in the implementation the place
that will then break.
I still am quite sure we have some horrible
misunderstanding, but that misunderstanding
is very unusual as I can't sense its roots.

All elf segments are arranged to their vaddr's
by the elf loader, not by the user. This is
obvious, undisputable fact. You always tell
me I want to lend that task to the user.
No I don't.
Why do you think I do?

>> If you can't, then you go away.
>> Do you accept that challenge?
> Asking a developer to go away not the way consensus is built.

I meant to say "after giving an ACK". :)
OK, sorry if it happened to be a bad joke.
But somehow I need to motivate Jonathon
or anyone else to make a proof of the statements
we have here. Otherwise we can't proceed.

I still leave the second part of a challenge:
if it is demonstrated that my patch doesn't
arrange the segments per vaddr's, I'll go
away forever. v9 has a few bugs in that area,
but not even nearly as bad as to not relocate
segments by vaddr's.


> If consensus can't be reached then the API will not be included
> in glibc.
>
> The real issue for me is that we are not providing a way for developers
> to manage the complexity of the *setup* that is required before dlmem()
> can operate reliably.

No, its not the case.
Its not the case and its not the case.
And I don't know what to do, if I can't
even ask someone to demonstrate why
its the case. :)
  
stsp March 29, 2023, 3:01 p.m. UTC | #8
29.03.2023 19:31, Adhemerval Zanella Netto пишет:
> This should be other way around since you are the one proposing a new
> interface and thus should make sure that potential raised concerns
> indeed does not affect it.
>
> Also, I would like to ask to tune down your tone. This kind of aggressive
> way to present technical topics, in a manner of adding 'challengers' to
> put pressure on person that raised concerns is not best way and usually
> make other developers to avoid engage further in the topic.
I understand and apologize to Jonathon,
but there is some misunderstanding that
can't be solved unless the problem is
demonstrated.
I very carefully inspected my v9 code and
indeed it had a few bugs in that area, but
they are not fatal and will all be covered
in v10. I very much doubt someone else
also found these bugs, so why my implementation
is blamed in not arranging the elf segments
by vaddr's, is essentially unclear!

Do you, Adhemerval, know why my impl is
suspected in not arranging an elf segments
properly?
I'll have to ask everyone personally it seems,
and maybe someone provides a clue finally.
  
Carlos O'Donell March 29, 2023, 3:20 p.m. UTC | #9
On 3/29/23 10:50, stsp wrote:
> 
> 29.03.2023 19:35, Carlos O'Donell пишет:
>> The fact that such binaries can be created is enough for me to raise
>> a sustained objection to the inclusion of dlmem().
> 
> Then someone needs to tell how exactly
> it can be created, and, more importantly,
> point in the implementation the place
> that will then break.

It is not a reviewers task to show this to you, it is only their task that
they tell you that it exists. The onus is strongly on the patch submitter to
produce evidence to the contrary. The reviewers on the list here are a scarce
resource, and they offer the experience and technical knowledge to help you.

Again, I see no solution that fixes the design problems of dlmem(), there might
be a solution that includes two APIs that each do half the work of fdlopen().

Are you looking for coaching or mentoring in the design of system interfaces
for dynamic loading?
  
stsp March 29, 2023, 3:34 p.m. UTC | #10
29.03.2023 20:20, Carlos O'Donell пишет:
> It is not a reviewers task to show this to you, it is only their task that
> they tell you that it exists. The onus is strongly on the patch submitter to
> produce evidence to the contrary. The reviewers on the list here are a scarce
> resource, and they offer the experience and technical knowledge to help you.

OK, I'll try to demonstrate the contrary,
but I am not even sure in what way you
want such a demonstration...
But lets try:

$ LD_LIBRARY_PATH=..:. ./tst-dlmem-fdlopen
unaligned buf gives buffer not aligned: Invalid argument
7fb413101000-7fb413102000 r--p 00000000 00:28 17195405 
/home/stas/src/glibc-dev/build/dlfcn/glreflib1.so
7fb413102000-7fb413103000 r-xp 00001000 00:28 17195405 
/home/stas/src/glibc-dev/build/dlfcn/glreflib1.so
7fb413103000-7fb413104000 r--p 00002000 00:28 17195405 
/home/stas/src/glibc-dev/build/dlfcn/glreflib1.so
7fb413104000-7fb413105000 r--p 00002000 00:28 17195405 
/home/stas/src/glibc-dev/build/dlfcn/glreflib1.so
7fb413105000-7fb413106000 rw-p 00003000 00:28 17195405 
/home/stas/src/glibc-dev/build/dlfcn/glreflib1.so

This is the output of my test-case in v9.
As you can see, I tried to code up the proof
already there.
"buffer not aligned" message comes from
the test that tries to submit an unaligned
buffer, so that's fine.
The next lines show you exactly the fact
that an elf was laid out per vaddr's. If it
wouldn't be the case then /proc/self/maps
would just have 1 reference to the .so file,
and in that example it does have 5.

Is this a proof?
If not - what kind of proof do you need?
  
stsp March 30, 2023, 8:09 a.m. UTC | #11
29.03.2023 19:10, Jonathon Anderson пишет:
> The second glaring issue (from my perspective) is that you are mmapping the entire file, instead of just the executable code. I have personally compiled up binaries where the DWARF debugging information was far larger than the code, one extreme case was a ~7.7GB binary of which merely ~130MB was .text. It is critical for performance that only that ~130MB is loaded from disk in the nominal case.
I assure you that only the PT_LOAD sections
will be read from disk. mmap() is not read(),
it doesn't prefetch the mapped pages.
I however admit (and may need to document)
that some absurdly large solibs, like 4Gb on
a 32bit system, may represent a problem.
Another thing I need to admit, is that I can't
implement dlopen_with_offset() very efficiently
for the very same reason: it loads the solib
from a container file that may be absurdly
large. So I'll demo-implement dlopen_with_offset2()
that has an extra "length" argument to specify
the solib length in a container file.
  

Patch

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..8bca644168 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-fdlopen
+CPPFLAGS-tst-dlmem-fdlopen.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-fdlopen.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/dlfcn.h b/dlfcn/dlfcn.h
index c5d192597d..87dc4932fd 100644
--- a/dlfcn/dlfcn.h
+++ b/dlfcn/dlfcn.h
@@ -68,6 +68,28 @@  extern void *dlsym (void *__restrict __handle,
 /* Like `dlopen', but request object to be allocated in a new namespace.  */
 extern void *dlmopen (Lmid_t __nsid, const char *__file, int __mode) __THROWNL;
 
+/* Callback for dlmem. */
+typedef void *
+(dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
+	          void *cookie);
+
+struct dlmem_args {
+  /* Optional name to associate with the loaded object. */
+  const char *soname;
+  /* Namespace where to load the object. */
+  Lmid_t nsid;
+  /* dlmem-specific flags. */
+  unsigned flags;
+  /* Optional premap callback. */
+  dlmem_premap_t *premap;
+  /* Optional argument for premap callback. */
+  void *cookie;
+};
+
+/* Like `dlmopen', but loads shared object from memory buffer.  */
+extern void *dlmem (const unsigned char *buffer, size_t size, int mode,
+		    struct dlmem_args *dlm_args);
+
 /* Find the run-time address in the shared object HANDLE refers to
    of the symbol called NAME with VERSION.  */
 extern void *dlvsym (void *__restrict __handle,
diff --git a/dlfcn/dlmem.c b/dlfcn/dlmem.c
new file mode 100644
index 0000000000..548f6f34eb
--- /dev/null
+++ b/dlfcn/dlmem.c
@@ -0,0 +1,102 @@ 
+/* 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;
+  struct dlmem_args *args;
+  /* The return value of dlmem_doit.  */
+  void *new;
+  /* Address of the caller.  */
+  const void *caller;
+};
+
+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->args,
+			    args->caller,
+			    __libc_argc, __libc_argv, __environ);
+}
+
+
+static void *
+dlmem_implementation (const unsigned char *buffer, size_t size, int mode,
+		      struct dlmem_args *dlm_args, void *dl_caller)
+{
+  struct _dlmem_args args;
+  args.buffer = buffer;
+  args.size = size;
+  args.mode = mode;
+  args.args = dlm_args;
+  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,
+	  struct dlmem_args *dlm_args)
+{
+  if (GLRO (dl_dlfcn_hook) != NULL)
+    return GLRO (dl_dlfcn_hook)->dlmem (buffer, size, mode, dlm_args,
+                                        RETURN_ADDRESS (0));
+  else
+    return dlmem_implementation (buffer, size, mode, dlm_args,
+				 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,
+	 struct dlmem_args *dlm_args, void *dl_caller)
+{
+  return dlmem_implementation (buffer, size, mode, dlm_args, dl_caller);
+}
+
+void *
+___dlmem (const unsigned char *buffer, size_t size, int mode,
+	  struct dlmem_args *dlm_args)
+{
+  return __dlmem (buffer, size, mode, dlm_args, RETURN_ADDRESS (0));
+}
+weak_alias (___dlmem, dlmem)
+static_link_warning (dlmem)
+#endif /* !SHARED */
diff --git a/dlfcn/tst-dlmem-fdlopen.c b/dlfcn/tst-dlmem-fdlopen.c
new file mode 100644
index 0000000000..db9b5cf54a
--- /dev/null
+++ b/dlfcn/tst-dlmem-fdlopen.c
@@ -0,0 +1,106 @@ 
+/* Test for fdlopen implementation on top of 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>
+#include <support/check.h>
+
+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;
+}
+
+
+#define TEST_FUNCTION do_test
+extern int do_test (void);
+
+int
+do_test (void)
+{
+  char cmd[256];
+  void *handle;
+  int (*sym) (void); /* We load ref1 from glreflib1.c.  */
+  Dl_info info;
+  int rc;
+  int fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
+  if (fd == -1)
+    error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
+  handle = fdlopen (fd, RTLD_NOW);
+  close (fd);
+  if (handle == NULL)
+    {
+      printf ("fdlopen failed, %s\n", dlerror());
+      exit (EXIT_FAILURE);
+    }
+
+  /* Check that the lib is properly mmap()ed, rather than memcpy()ed.
+     This may fail on linux kernels <5.13. */
+  snprintf (cmd, sizeof(cmd), "grep glreflib1.so /proc/%i/maps", getpid());
+  rc = system (cmd);
+  TEST_COMPARE (rc, 0);
+
+  sym = dlsym (handle, "ref1");
+  if (sym == NULL)
+    error (EXIT_FAILURE, 0, "dlsym failed");
+
+  memset (&info, 0, sizeof (info));
+  rc = dladdr (sym, &info);
+  if (rc == 0)
+    error (EXIT_FAILURE, 0, "dladdr failed");
+
+  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);
+
+  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");
+  return 0;
+}
+
+
+#include <support/test-driver.c>
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 7175b99962..2550173f48 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -75,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>
@@ -2353,6 +2354,174 @@  _dl_map_object (struct link_map *loader, const char *name,
   return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid);
 }
 
+static void *
+do_memremap (void *addr, size_t length, int prot, int flags,
+             void *arg, off_t offset)
+{
+  const struct dlmem_fbuf *fb = arg;
+
+  assert (flags & MAP_FIXED);
+  if (offset < fb->len)
+    {
+      size_t to_copy = length;
+      if (offset + to_copy > fb->len)
+        to_copy = fb->len - offset;
+#ifdef MREMAP_DONTUNMAP
+      void *addr2 = __mremap ((void *) (fb->buf + offset), to_copy, to_copy,
+                              MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_DONTUNMAP,
+                              addr);
+      /* MREMAP_DONTUNMAP introduced in linux-5.7, but only works for
+         file-based maps since commit a460938 went in 5.13.
+         So have a fall-back. */
+      if (addr2 == MAP_FAILED)
+        memcpy (addr, fb->buf + offset, to_copy);
+#else
+      /* MREMAP_DONTUNMAP is not always available. This is a fall-back. */
+      memcpy (addr, fb->buf + offset, to_copy);
+#endif
+    }
+  if (__mprotect (addr, length, prot) == -1)
+    return MAP_FAILED;
+  return addr;
+}
+
+static void *
+do_dlmem_premap (void *mappref, size_t maplength, size_t mapalign,
+		 void *cookie)
+{
+  struct dlmem_fbuf *fb = cookie;
+  void *ret = MAP_FAILED;
+
+  if (fb->dlm_args && fb->dlm_args->premap)
+    ret = fb->dlm_args->premap (mappref, maplength, mapalign,
+                                fb->dlm_args->cookie);
+  if (ret == MAP_FAILED)
+    ret = (void *) _dl_map_segment ((ElfW(Addr)) mappref, maplength,
+                                    mapalign);
+  return ret;
+}
+
+static ssize_t
+do_pread_memcpy (void *arg, void *buf, size_t count, off_t offset)
+{
+  struct dlmem_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 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;
+  char *realname;
+  /* 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));
+
+  if (name && *name)
+    {
+      /* Look for this name among those already loaded.  */
+      l = _dl_check_loaded (name, nsid);
+      if (l)
+        return l;
+    }
+
+  /* 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);
+
+  /* _dl_new_object() treats "" separately and doesn't free it. */
+  realname = *name ? __strdup (name) : (char *) "";
+  /* Enter the new object in the list of loaded objects.  */
+  l = _dl_new_object (realname, 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_memremap, 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 e777da5838..09b2878260 100644
--- a/elf/dl-load.h
+++ b/elf/dl-load.h
@@ -106,6 +106,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);
 
 /* 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,
diff --git a/elf/dl-main.h b/elf/dl-main.h
index 344a87d5e8..e60fafaeb6 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -104,6 +104,13 @@  struct dl_main_state
   bool version_info;
 };
 
+struct dlmem_fbuf
+{
+  ssize_t len;
+  const unsigned char *buf;
+  struct dlmem_args *dlm_args;
+};
+
 /* 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.  */
@@ -112,6 +119,11 @@  __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.  */
diff --git a/elf/dl-open.c b/elf/dl-open.c
index f3886c21bc..7c7bb2ef4f 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -953,6 +953,19 @@  _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
                      __dl_map_object);
 }
 
+void *
+_dl_mem (const unsigned char *buffer, size_t size, int mode,
+	 struct dlmem_args *dlm_args, const void *caller_dlopen,
+	 int argc, char *argv[], char *env[])
+{
+  struct dlmem_fbuf fb = { .buf = buffer, .len = size, .dlm_args = dlm_args };
+  Lmid_t nsid = dlm_args ? dlm_args->nsid : LM_ID_BASE;
+  const char *file = (dlm_args && dlm_args->soname) ? dlm_args->soname : "";
+
+  return do_dl_open (file, &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/rtld.c b/elf/rtld.c
index f82fbeb132..1877ec9f16 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,
diff --git a/include/dlfcn.h b/include/dlfcn.h
index ae25f05303..6205cf407d 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,
+		  struct dlmem_args *dlm_args, 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,
+		      struct dlmem_args *dlm_args, 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..abca95b18c 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -669,6 +669,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,
+		    struct dlmem_args *dlm_args, const void *caller_dlopen,
+		    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
@@ -1249,6 +1252,12 @@  extern void *_dl_open (const char *name, int mode, const void *caller,
 		       Lmid_t nsid, int argc, char *argv[], char *env[])
      attribute_hidden;
 
+/* Open shared object from memory buffer. */
+extern void *_dl_mem (const unsigned char *buffer, size_t size, int mode,
+		      struct dlmem_args *dlm_args, const void *caller,
+		      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
    old scope, OLD can't be freed until no thread is using it.  */
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index ed0c4789eb..1880004336 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2326,6 +2326,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 0e2d9c3045..c4d31cabed 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2665,3 +2665,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 f1bec1978d..93b37a1e1d 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2774,6 +2774,7 @@  GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf 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 aa874b88d0..9fec2308c3 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2426,3 +2426,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 afbd57da6f..bd0f04e58a 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -546,6 +546,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 e7364cd3fe..dccc49c8d6 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -543,6 +543,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 913fa59215..0df8524ac0 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2702,3 +2702,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 43af3a9811..fa4680e97a 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2651,6 +2651,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 af72f8fab0..bfb1bde49d 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2835,6 +2835,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 48cbb0fa50..e53505abe0 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2600,6 +2600,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 c15884bb0b..9f56cbdcab 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2186,3 +2186,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_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 3738db81df..d5443b1198 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -547,6 +547,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 ed13627752..640c8b8c4a 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2778,6 +2778,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 8357738621..79b400efc6 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2751,3 +2751,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 58c5da583d..38b4098950 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2748,3 +2748,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 d3741945cd..9a4f909a25 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2743,6 +2743,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 5319fdc204..c8b9f85fdb 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2741,6 +2741,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 1743ea6eb9..0887b67394 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2749,6 +2749,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 9b1f53c6ac..1c3a6f4bee 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2651,6 +2651,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 ae1c6ca1b5..31b23859a4 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2790,3 +2790,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 a7c572c947..59f4aa7766 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2172,3 +2172,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 074fa031a7..d715d0ae97 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2817,6 +2817,7 @@  GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf 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 dfcb4bd2d5..3addcf3d17 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2850,6 +2850,7 @@  GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf 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 63bbccf3f9..5365978277 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2571,6 +2571,7 @@  GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf 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 ab85fd61ef..f0af576192 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2885,3 +2885,4 @@  GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf 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 b716f5c763..941fd40ea8 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2428,3 +2428,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 774e777b65..74f58439ad 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2628,3 +2628,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 8625135c48..cf3a10aa76 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2815,6 +2815,7 @@  GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf 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 d00c7eb262..2ad97f87b2 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2608,6 +2608,7 @@  GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf 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 b63037241d..7c81b94953 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2658,6 +2658,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 d80055617d..0493d6b456 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2655,6 +2655,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 5be55c11d2..6fd09f6499 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2810,6 +2810,7 @@  GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf 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 475fdaae15..24dbc4801f 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2623,6 +2623,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 6cfb928bc8..522ca8e8aa 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2574,6 +2574,7 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf 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 c735097172..42b170c805 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2680,3 +2680,4 @@  GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F