public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v10 0/12] implement dlmem() function
@ 2023-04-03  9:04 Stas Sergeev
  2023-04-03  9:04 ` [PATCH 01/12] elf: split _dl_map_object_from_fd() into reusable parts Stas Sergeev
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch-set implements the dlmem() function that allows to load
the solib from the file-mapped or anonymously-shared memory buffer.
Generic memory buffer is also supported with DLMEM_GENBUF_SRC flag,
but it should not be preferred. Private anonymous mapping used as
a source buffer, also needs this flag to be set.

dlmem() suits as a building block for implementing the functions like
fdlopen() and dlopen_with_offset(), which are demo-implemented in a
test-case called tst-dlmem-extfns.
The reasons why it suits well for such file-based loaders, are below:
1. It correctly handles the file association of the original solib
   buffer if it was mmap'ed from a file (unless DLMEM_GENBUF_SRC
   of DLMEM_DONTREPLACE flag is set).
2. It allows to provide a solib name, which can be the file name.

With the above properties, the "direct" implementation of these functions
gives no advantages over implementing them with dlmem().

In addition, dlmem() has lots of optional functionality for the fine-grained
control over the loading process. It allows you to set nsid (like dlmopen()),
specify the solib relocation address and even relocate the solib into
the user's buffer. That "advanced" functionality is only needed for the
very specific use-cases, like virtualized environments where the relocation
address may have a special constraints, eg MAP_32BIT. In all other cases
it is advised to set the "dlm_args" pointer of dlmem() call to NULL, but
see "Limitations" below to find out when its not the case.

The API looks as below:

/* Callback for dlmem. */
typedef void *
(dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
	          void *cookie);

/* Do not replace mapping created by premap callback.
   dlmem() will then use memcpy(). */
#define DLMEM_DONTREPLACE 1
/* Treat source memory buffer as a generic unaligned buffer, rather
   than a file-backed or anonymously-shared mapping. Anonymous private
   mapping also needs this flag to be set. */
#define DLMEM_GENBUF_SRC 2

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 int 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);

Advanced functionality:

In most cases dlm_args should just be set to NULL. It provides the
advanced functionality, most of which is obvious (soname, nsid).
The optional premap callback allows to set the relocation address for
the solib by mapping the destination space and returning its address.
More so, if DLMEM_DONTREPLACE flag is used, then the mapping
established by the premap callback, will not be replaced with the
file-backed mapping. In that case dlmem() have to use memcpy(), which
is likely even faster than mmaps() but doesn't end up with the proper
/proc/self/map_files or /proc/self/maps entries. So for example if the
premap callback uses MAP_SHARED, then with the use of the DLMEM_DONTREPLACE
flag you can get your solib relocated into a shared memory buffer.
Note that the premap callback may be called under glibc locks, so it
should restrict itself to the syscall functionality. Certainly no libdl
functions can be used. mmap(), shm_open(), open(), ftruncate(),
memfd_create() should be the sufficient set of functions that may
ever be in use in a premap callback, but in most cases the callback
should just be disabled.


Limitations:

- If you need to load the solib from anonymously-mapped buffer, you need
  to use MAP_SHARED|MAP_ANONYMOUS mmap flags when creating that buffer.
  If it is not possible in your use-case and the buffer was created
  with MAP_PRIVATE|MAP_ANONYMOUS flags, then DLMEM_GENBUF_SRC flag
  needs to be set when calling dlmem().
  Failure to follow that guide-line results in an UB (loader will not
  be able to properly lay out an elf segments).

- If you use a private file-backed mapping, then it shouldn't be
  modified by hands before passing to dlmem(). I.e. you can't apply
  mprotect() to it to change protection bits, and you can't apply
  memmove() to it to move the solib to the beginning of the buffer,
  and so on. dlmem() can only work with "virgin" private file-backed
  mappings. You can set DLMEM_GENBUF_SRC flag as a work-around if
  the mapping is already corrupted.
  Failure to follow that guide-line results in an UB (loader will not
  be able to properly lay out an elf segments).

- The need of mapping the entire solib (with debug info etc) may
  represent a problem on a 32bit architectures if the solib has an
  absurdly large size, like 3Gb or more.

- For the very same reason the efficient implementation of Android's
  dlopen_with_offset() is difficult, as in that case you'd need to
  map the entire file container, starting from the needed offset.
  The demo implementation in this patch implements dlopen_with_offset4()
  that has an additional "length" argument where the solib length
  should be passed.

- As linux doesn't implement MAP_UNALIGNED as some unixes did, the
  efficient implementation of dlopen_with_offset4() is difficult
  if the offset is not page-aligned. Demo in this example fixes the
  alignment by hands, using the MAP_SHARED|MAP_ANONYMOUS intermediate
  buffer. The alignment cannot be fixed in an existing buffer with
  memmove(), as that will make the file-backed mapping unacceptable
  for the use with dlmem(). I suspect that google's dlopen_with_offset()
  has similar limitation because mmap() with unaligned offset is
  not possible in any implementation, be it a "direct" implementation
  or "over-dlmem" implementation.


Changes in v10:
- addressed review comments of Adhemerval Zanella
- moved refactor patches to the beginning of the serie to simplify review
- fixed a few bugs in an elf relocation machinery after various hot discussions
- added a new test tst-dlmem-extfns that demo-implements dlopen_with_offset4()
  and fdlopen()
- studied and documented all limitations, most importantly those leading to UB
- better documented premap callback as suggested by Szabolcs Nagy
- added DLMEM_GENBUF_SRC flag for unaligned generic memory buffers

Changes in v9:
- use "zero-copy" machinery instead of memcpy(). It works on linux 5.13
  and newer, falling back to memcpy() otherwise. Suggested by Florian Weimer.
- implement fdlopen() using the above functionality. It is in a new test
  tst-dlmem-fdlopen. Suggested by Carlos O'Donell.
- add DLMEM_DONTREPLACE flag that doesn't replace the backing-store mapping.
  It switches back to memcpy(). Test-case is called tst-dlmem-shm.

Changes in v8:
- drop audit machinery and instead add an extra arg (optional pointer
  to a struct) to dlmem() itself that allows to install a custom premap
  callback or to specify nsid. Audit machinery was meant to allow
  controling over the pre-existing APIs like dlopen(), but if someone
  ever needs such extensions to dlopen(), he can trivially implement
  dlopen() on top of dlmem().

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

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

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

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

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

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

-- 
2.37.2


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

* [PATCH 01/12] elf: split _dl_map_object_from_fd() into reusable parts
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 02/12] elf: split open_verify() " Stas Sergeev
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This is mostly a mechanical split, with just a very minor moves
of 2 small code fragments. This change should introduce no
functional differences.

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c | 329 ++++++++++++++++++++++++++++----------------------
 1 file changed, 185 insertions(+), 144 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 9a0e40c0e9..6b2c3ac6bf 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -929,137 +929,46 @@ _dl_process_pt_gnu_property (struct link_map *l, int fd, const ElfW(Phdr) *ph)
     }
 }
 
+static void
+_dl_process_phdrs (struct link_map *l, int fd)
+{
+  const ElfW(Phdr) *ph;
 
-/* Map in the shared object NAME, actually located in REALNAME, and already
-   opened on FD.  */
+  /* Process program headers again after load segments are mapped in
+     case processing requires accessing those segments.  Scan program
+     headers backward so that PT_NOTE can be skipped if PT_GNU_PROPERTY
+     exits.  */
+  for (ph = &l->l_phdr[l->l_phnum]; ph != l->l_phdr; --ph)
+    switch (ph[-1].p_type)
+      {
+      case PT_NOTE:
+	_dl_process_pt_note (l, fd, &ph[-1]);
+	break;
+      case PT_GNU_PROPERTY:
+	_dl_process_pt_gnu_property (l, fd, &ph[-1]);
+	break;
+      }
+}
 
-#ifndef EXTERNAL_MAP_FROM_FD
-static
-#endif
-struct link_map *
-_dl_map_object_from_fd (const char *name, const char *origname, int fd,
-			struct filebuf *fbp, char *realname,
-			struct link_map *loader, int l_type, int mode,
-			void **stack_endp, Lmid_t nsid)
+static int
+_dl_map_object_1 (struct link_map *l, int fd,
+                  struct filebuf *fbp,
+                  int mode, struct link_map *loader,
+                  void **stack_endp, int *errval_p,
+                  const char **errstring_p)
 {
-  struct link_map *l = NULL;
   const ElfW(Ehdr) *header;
   const ElfW(Phdr) *phdr;
   const ElfW(Phdr) *ph;
   size_t maplength;
   int type;
   /* Initialize to keep the compiler happy.  */
-  const char *errstring = NULL;
-  int errval = 0;
-
-  /* Get file information.  To match the kernel behavior, do not fill
-     in this information for the executable in case of an explicit
-     loader invocation.  */
-  struct r_file_id id;
-  if (mode & __RTLD_OPENEXEC)
-    {
-      assert (nsid == LM_ID_BASE);
-      memset (&id, 0, sizeof (id));
-    }
-  else
-    {
-      if (__glibc_unlikely (!_dl_get_file_id (fd, &id)))
-	{
-	  errstring = N_("cannot stat shared object");
-	lose_errno:
-	  errval = errno;
-	lose:
-	  /* The file might already be closed.  */
-	  if (fd != -1)
-	    __close_nocancel (fd);
-	  if (l != NULL && l->l_map_start != 0)
-	    _dl_unmap_segments (l);
-	  if (l != NULL && l->l_origin != (char *) -1l)
-	    free ((char *) l->l_origin);
-	  if (l != NULL && !l->l_libname->dont_free)
-	    free (l->l_libname);
-	  if (l != NULL && l->l_phdr_allocated)
-	    free ((void *) l->l_phdr);
-	  free (l);
-	  free (realname);
-	  _dl_signal_error (errval, name, NULL, errstring);
-	}
-
-      /* Look again to see if the real name matched another already loaded.  */
-      for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
-	if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
-	  {
-	    /* The object is already loaded.
-	       Just bump its reference count and return it.  */
-	    __close_nocancel (fd);
-
-	    /* If the name is not in the list of names for this object add
-	       it.  */
-	    free (realname);
-	    add_name_to_object (l, name);
-
-	    return l;
-	  }
-    }
-
-#ifdef SHARED
-  /* When loading into a namespace other than the base one we must
-     avoid loading ld.so since there can only be one copy.  Ever.  */
-  if (__glibc_unlikely (nsid != LM_ID_BASE)
-      && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id)
-	  || _dl_name_match_p (name, &GL(dl_rtld_map))))
-    {
-      /* This is indeed ld.so.  Create a new link_map which refers to
-	 the real one for almost everything.  */
-      l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
-      if (l == NULL)
-	goto fail_new;
-
-      /* Refer to the real descriptor.  */
-      l->l_real = &GL(dl_rtld_map);
-
-      /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen().  */
-      l->l_addr = l->l_real->l_addr;
-      l->l_ld = l->l_real->l_ld;
-
-      /* No need to bump the refcount of the real object, ld.so will
-	 never be unloaded.  */
-      __close_nocancel (fd);
-
-      /* Add the map for the mirrored object to the object list.  */
-      _dl_add_to_namespace_list (l, nsid);
-
-      return l;
-    }
-#endif
-
-  if (mode & RTLD_NOLOAD)
-    {
-      /* We are not supposed to load the object unless it is already
-	 loaded.  So return now.  */
-      free (realname);
-      __close_nocancel (fd);
-      return NULL;
-    }
-
-  /* Print debugging message.  */
-  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
-    _dl_debug_printf ("file=%s [%lu];  generating link map\n", name, nsid);
+#define errstring (*errstring_p)
+#define errval (*errval_p)
 
   /* This is the ELF header.  We read it in `open_verify'.  */
   header = (void *) fbp->buf;
 
-  /* Enter the new object in the list of loaded objects.  */
-  l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
-  if (__glibc_unlikely (l == NULL))
-    {
-#ifdef SHARED
-    fail_new:
-#endif
-      errstring = N_("cannot create shared object descriptor");
-      goto lose_errno;
-    }
-
   /* Extract the remaining details we need from the ELF header
      and then read in the program header table.  */
   l->l_entry = header->e_entry;
@@ -1083,7 +992,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
    /* On most platforms presume that PT_GNU_STACK is absent and the stack is
     * executable.  Other platforms default to a nonexecutable stack and don't
     * need PT_GNU_STACK to do so.  */
-   unsigned int stack_flags = DEFAULT_STACK_PERMS;
+  unsigned int stack_flags = DEFAULT_STACK_PERMS;
 
   {
     /* Scan the program header table, collecting its load commands.  */
@@ -1361,30 +1270,6 @@ cannot enable executable stack as shared object requires");
   if (l->l_tls_initimage != NULL)
     l->l_tls_initimage = (char *) l->l_tls_initimage + l->l_addr;
 
-  /* Process program headers again after load segments are mapped in
-     case processing requires accessing those segments.  Scan program
-     headers backward so that PT_NOTE can be skipped if PT_GNU_PROPERTY
-     exits.  */
-  for (ph = &l->l_phdr[l->l_phnum]; ph != l->l_phdr; --ph)
-    switch (ph[-1].p_type)
-      {
-      case PT_NOTE:
-	_dl_process_pt_note (l, fd, &ph[-1]);
-	break;
-      case PT_GNU_PROPERTY:
-	_dl_process_pt_gnu_property (l, fd, &ph[-1]);
-	break;
-      }
-
-  /* We are done mapping in the file.  We no longer need the descriptor.  */
-  if (__glibc_unlikely (__close_nocancel (fd) != 0))
-    {
-      errstring = N_("cannot close file descriptor");
-      goto lose_errno;
-    }
-  /* Signal that we closed the file.  */
-  fd = -1;
-
   /* Failures before this point are handled locally via lose.
      There are no more failures in this function until return,
      to change that the cleanup handling needs to be updated.  */
@@ -1409,6 +1294,22 @@ cannot enable executable stack as shared object requires");
 			   (unsigned long int) l->l_phdr,
 			   (int) sizeof (void *) * 2, l->l_phnum);
 
+  return 0;
+
+lose_errno:
+  errval = errno;
+lose:
+  return -1;
+
+#undef errval
+#undef errstring
+}
+
+static void
+_dl_map_object_2 (struct link_map *l, int mode,
+                  struct r_file_id id, const char *origname,
+                  Lmid_t nsid)
+{
   /* Set up the symbol hash table.  */
   _dl_setup_hash (l);
 
@@ -1510,10 +1411,150 @@ cannot enable executable stack as shared object requires");
   if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
     _dl_audit_objopen (l, nsid);
 #endif
+}
+
+/* Map in the shared object NAME, actually located in REALNAME, and already
+   opened on FD.  */
+
+#ifndef EXTERNAL_MAP_FROM_FD
+static
+#endif
+struct link_map *
+_dl_map_object_from_fd (const char *name, const char *origname, int fd,
+			struct filebuf *fbp, char *realname,
+			struct link_map *loader, int l_type, int mode,
+			void **stack_endp, Lmid_t nsid)
+{
+  struct link_map *l = NULL;
+  /* Initialize to keep the compiler happy.  */
+  const char *errstring = NULL;
+  int errval = 0;
+
+  /* Get file information.  To match the kernel behavior, do not fill
+     in this information for the executable in case of an explicit
+     loader invocation.  */
+  struct r_file_id id;
+  if (mode & __RTLD_OPENEXEC)
+    {
+      assert (nsid == LM_ID_BASE);
+      memset (&id, 0, sizeof (id));
+    }
+  else
+    {
+      if (__glibc_unlikely (!_dl_get_file_id (fd, &id)))
+	{
+	  errstring = N_("cannot stat shared object");
+	lose_errno:
+	  errval = errno;
+	lose:
+	  /* The file might already be closed.  */
+	  if (fd != -1)
+	    __close_nocancel (fd);
+	  if (l != NULL && l->l_map_start != 0)
+	    _dl_unmap_segments (l);
+	  if (l != NULL && l->l_origin != (char *) -1l)
+	    free ((char *) l->l_origin);
+	  if (l != NULL && !l->l_libname->dont_free)
+	    free (l->l_libname);
+	  if (l != NULL && l->l_phdr_allocated)
+	    free ((void *) l->l_phdr);
+	  free (l);
+	  free (realname);
+
+	  _dl_signal_error (errval, name, NULL, errstring);
+	}
+
+      /* Look again to see if the real name matched another already loaded.  */
+      for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
+	if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
+	  {
+	    /* The object is already loaded.
+	       Just bump its reference count and return it.  */
+	    __close_nocancel (fd);
+
+	    /* If the name is not in the list of names for this object add
+	       it.  */
+	    free (realname);
+	    add_name_to_object (l, name);
+
+	    return l;
+	  }
+    }
+
+#ifdef SHARED
+  /* When loading into a namespace other than the base one we must
+     avoid loading ld.so since there can only be one copy.  Ever.  */
+  if (__glibc_unlikely (nsid != LM_ID_BASE)
+      && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id)
+	  || _dl_name_match_p (name, &GL(dl_rtld_map))))
+    {
+      /* This is indeed ld.so.  Create a new link_map which refers to
+	 the real one for almost everything.  */
+      l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
+      if (l == NULL)
+	goto fail_new;
+
+      /* Refer to the real descriptor.  */
+      l->l_real = &GL(dl_rtld_map);
+
+      /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen().  */
+      l->l_addr = l->l_real->l_addr;
+      l->l_ld = l->l_real->l_ld;
+
+      /* No need to bump the refcount of the real object, ld.so will
+	 never be unloaded.  */
+      __close_nocancel (fd);
+
+      /* Add the map for the mirrored object to the object list.  */
+      _dl_add_to_namespace_list (l, nsid);
+
+      return l;
+    }
+#endif
+
+  if (mode & RTLD_NOLOAD)
+    {
+      /* We are not supposed to load the object unless it is already
+	 loaded.  So return now.  */
+      free (realname);
+      __close_nocancel (fd);
+      return NULL;
+    }
+
+  /* Print debugging message.  */
+  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+    _dl_debug_printf ("file=%s [%lu];  generating link map\n", name, nsid);
+
+  /* Enter the new object in the list of loaded objects.  */
+  l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
+  if (__glibc_unlikely (l == NULL))
+    {
+#ifdef SHARED
+    fail_new:
+#endif
+      errstring = N_("cannot create shared object descriptor");
+      goto lose_errno;
+    }
+
+  if (_dl_map_object_1 (l, fd, fbp, mode, loader, stack_endp, &errval,
+                        &errstring))
+    goto lose;
+
+  _dl_process_phdrs (l, fd);
 
+  /* We are done mapping in the file.  We no longer need the descriptor.  */
+  if (__glibc_unlikely (__close_nocancel (fd) != 0))
+    {
+      errstring = N_("cannot close file descriptor");
+      goto lose_errno;
+    }
+  /* Signal that we closed the file.  */
+  fd = -1;
+
+  _dl_map_object_2 (l, mode, id, origname, nsid);
   return l;
 }
-\f
+
 /* Print search path.  */
 static void
 print_search_path (struct r_search_path_elem **list,
-- 
2.37.2


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

* [PATCH 02/12] elf: split open_verify() into reusable parts
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
  2023-04-03  9:04 ` [PATCH 01/12] elf: split _dl_map_object_from_fd() into reusable parts Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 03/12] elf: split _dl_check_loaded() from _dl_map_object Stas Sergeev
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This is almost a mechanical refactoring that splits open_verify()
into 2 parts.

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c | 337 ++++++++++++++++++++++++++------------------------
 1 file changed, 175 insertions(+), 162 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 6b2c3ac6bf..7a939cb8c1 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1596,19 +1596,11 @@ print_search_path (struct r_search_path_elem **list,
   else
     _dl_debug_printf_c ("\t\t(%s)\n", what);
 }
-\f
-/* Open a file and verify it is an ELF file for this architecture.  We
-   ignore only ELF files for other architectures.  Non-ELF files and
-   ELF files with different header information cause fatal errors since
-   this could mean there is something wrong in the installation and the
-   user might want to know about this.
 
-   If FD is not -1, then the file is already open and FD refers to it.
-   In that case, FD is consumed for both successful and error returns.  */
 static int
-open_verify (const char *name, int fd,
-             struct filebuf *fbp, struct link_map *loader,
-	     int whatcode, int mode, bool *found_other_class, bool free_name)
+do_open_verify (const char *name, int fd,
+                struct filebuf *fbp, struct link_map *loader,
+                bool *found_other_class, bool free_name)
 {
   /* This is the expected ELF header.  */
 #define ELF32_CLASS ELFCLASS32
@@ -1635,7 +1627,170 @@ open_verify (const char *name, int fd,
   /* Initialize it to make the compiler happy.  */
   const char *errstring = NULL;
   int errval = 0;
+  ElfW(Ehdr) *ehdr;
+  ElfW(Phdr) *phdr;
+  size_t maplength;
+
+  /* We successfully opened the file.  Now verify it is a file
+   we can use.  */
+  __set_errno (0);
+  fbp->len = 0;
+  assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
+  /* Read in the header.  */
+  do
+    {
+      ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len,
+					sizeof (fbp->buf) - fbp->len);
+      if (retlen <= 0)
+        break;
+      fbp->len += retlen;
+    }
+  while (__glibc_unlikely (fbp->len < sizeof (ElfW(Ehdr))));
+
+  /* This is where the ELF header is loaded.  */
+  ehdr = (ElfW(Ehdr) *) fbp->buf;
+
+  /* Now run the tests.  */
+  if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr))))
+    {
+      errval = errno;
+      errstring = (errval == 0
+	       ? N_("file too short") : N_("cannot read file data"));
+    lose:
+      if (free_name)
+        {
+          char *realname = (char *) name;
+          name = strdupa (realname);
+          free (realname);
+        }
+      _dl_signal_error (errval, name, NULL, errstring);
+      return -1;
+    }
+
+  /* See whether the ELF header is what we expect.  */
+  if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected,
+					    EI_ABIVERSION)
+			|| !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
+						  ehdr->e_ident[EI_ABIVERSION])
+			|| memcmp (&ehdr->e_ident[EI_PAD],
+				   &expected[EI_PAD],
+				    EI_NIDENT - EI_PAD) != 0))
+    {
+      /* Something is wrong.  */
+      const Elf32_Word *magp = (const void *) ehdr->e_ident;
+      if (*magp !=
+#if BYTE_ORDER == LITTLE_ENDIAN
+          ((ELFMAG0 << (EI_MAG0 * 8))
+           | (ELFMAG1 << (EI_MAG1 * 8))
+           | (ELFMAG2 << (EI_MAG2 * 8))
+           | (ELFMAG3 << (EI_MAG3 * 8)))
+#else
+          ((ELFMAG0 << (EI_MAG3 * 8))
+           | (ELFMAG1 << (EI_MAG2 * 8))
+           | (ELFMAG2 << (EI_MAG1 * 8))
+           | (ELFMAG3 << (EI_MAG0 * 8)))
+#endif
+          )
+        errstring = N_("invalid ELF header");
+
+      else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS))
+        {
+          /* This is not a fatal error.  On architectures where
+             32-bit and 64-bit binaries can be run this might
+             happen.  */
+          *found_other_class = true;
+          __set_errno (ENOENT);
+          return -1;
+        }
+      else if (ehdr->e_ident[EI_DATA] != byteorder)
+        {
+          if (BYTE_ORDER == BIG_ENDIAN)
+            errstring = N_("ELF file data encoding not big-endian");
+          else
+            errstring = N_("ELF file data encoding not little-endian");
+        }
+      else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
+        errstring
+          = N_("ELF file version ident does not match current one");
+      /* XXX We should be able so set system specific versions which are
+         allowed here.  */
+      else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI]))
+        errstring = N_("ELF file OS ABI invalid");
+      else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
+				      ehdr->e_ident[EI_ABIVERSION]))
+        errstring = N_("ELF file ABI version invalid");
+      else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD],
+		       EI_NIDENT - EI_PAD) != 0)
+        errstring = N_("nonzero padding in e_ident");
+      else
+        /* Otherwise we don't know what went wrong.  */
+        errstring = N_("internal error");
+
+      goto lose;
+    }
+
+  if (__glibc_unlikely (ehdr->e_version != EV_CURRENT))
+    {
+      errstring = N_("ELF file version does not match current one");
+      goto lose;
+    }
+  if (! __glibc_likely (elf_machine_matches_host (ehdr)))
+    {
+      __set_errno (ENOENT);
+      return -1;
+    }
+  else if (__glibc_unlikely (ehdr->e_type != ET_DYN
+				 && ehdr->e_type != ET_EXEC))
+    {
+      errstring = N_("only ET_DYN and ET_EXEC can be loaded");
+      goto lose;
+    }
+  else if (__glibc_unlikely (ehdr->e_phentsize != sizeof (ElfW(Phdr))))
+    {
+      errstring = N_("ELF file's phentsize not the expected size");
+      goto lose;
+    }
+
+  maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
+  if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
+    phdr = (void *) (fbp->buf + ehdr->e_phoff);
+  else
+    {
+      phdr = alloca (maplength);
+      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
+					   ehdr->e_phoff) != maplength)
+        {
+          errval = errno;
+          errstring = N_("cannot read file data");
+          goto lose;
+        }
+    }
+
+  if (__glibc_unlikely (elf_machine_reject_phdr_p
+		       (phdr, ehdr->e_phnum, fbp->buf, fbp->len,
+			loader, -1)))
+    {
+      __set_errno (ENOENT);
+      return -1;
+    }
+
+  return 0;
+}
+
+
+/* Open a file and verify it is an ELF file for this architecture.  We
+   ignore only ELF files for other architectures.  Non-ELF files and
+   ELF files with different header information cause fatal errors since
+   this could mean there is something wrong in the installation and the
+   user might want to know about this.
 
+   If FD is not -1, then the file is already open and FD refers to it.
+   In that case, FD is consumed for both successful and error returns.  */
+static int
+open_verify (const char *name, int fd,
+             struct filebuf *fbp, struct link_map *loader,
+	     int whatcode, int mode, bool *found_other_class, bool free_name)
+{
 #ifdef SHARED
   /* Give the auditing libraries a chance.  */
   if (__glibc_unlikely (GLRO(dl_naudit) > 0))
@@ -1661,161 +1816,19 @@ open_verify (const char *name, int fd,
 
   if (fd != -1)
     {
-      ElfW(Ehdr) *ehdr;
-      ElfW(Phdr) *phdr;
-      size_t maplength;
-
-      /* We successfully opened the file.  Now verify it is a file
-	 we can use.  */
-      __set_errno (0);
-      fbp->len = 0;
-      assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
-      /* Read in the header.  */
-      do
-	{
-	  ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len,
-					    sizeof (fbp->buf) - fbp->len);
-	  if (retlen <= 0)
-	    break;
-	  fbp->len += retlen;
-	}
-      while (__glibc_unlikely (fbp->len < sizeof (ElfW(Ehdr))));
-
-      /* This is where the ELF header is loaded.  */
-      ehdr = (ElfW(Ehdr) *) fbp->buf;
-
-      /* Now run the tests.  */
-      if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr))))
-	{
-	  errval = errno;
-	  errstring = (errval == 0
-		       ? N_("file too short") : N_("cannot read file data"));
-	lose:
-	  if (free_name)
-	    {
-	      char *realname = (char *) name;
-	      name = strdupa (realname);
-	      free (realname);
-	    }
-	  __close_nocancel (fd);
-	  _dl_signal_error (errval, name, NULL, errstring);
-	}
-
-      /* See whether the ELF header is what we expect.  */
-      if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected,
-						EI_ABIVERSION)
-			    || !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
-						      ehdr->e_ident[EI_ABIVERSION])
-			    || memcmp (&ehdr->e_ident[EI_PAD],
-				       &expected[EI_PAD],
-				       EI_NIDENT - EI_PAD) != 0))
-	{
-	  /* Something is wrong.  */
-	  const Elf32_Word *magp = (const void *) ehdr->e_ident;
-	  if (*magp !=
-#if BYTE_ORDER == LITTLE_ENDIAN
-	      ((ELFMAG0 << (EI_MAG0 * 8))
-	       | (ELFMAG1 << (EI_MAG1 * 8))
-	       | (ELFMAG2 << (EI_MAG2 * 8))
-	       | (ELFMAG3 << (EI_MAG3 * 8)))
-#else
-	      ((ELFMAG0 << (EI_MAG3 * 8))
-	       | (ELFMAG1 << (EI_MAG2 * 8))
-	       | (ELFMAG2 << (EI_MAG1 * 8))
-	       | (ELFMAG3 << (EI_MAG0 * 8)))
-#endif
-	      )
-	    errstring = N_("invalid ELF header");
-
-	  else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS))
-	    {
-	      /* This is not a fatal error.  On architectures where
-		 32-bit and 64-bit binaries can be run this might
-		 happen.  */
-	      *found_other_class = true;
-	      __close_nocancel (fd);
-	      __set_errno (ENOENT);
-	      return -1;
-	    }
-	  else if (ehdr->e_ident[EI_DATA] != byteorder)
-	    {
-	      if (BYTE_ORDER == BIG_ENDIAN)
-		errstring = N_("ELF file data encoding not big-endian");
-	      else
-		errstring = N_("ELF file data encoding not little-endian");
-	    }
-	  else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
-	    errstring
-	      = N_("ELF file version ident does not match current one");
-	  /* XXX We should be able so set system specific versions which are
-	     allowed here.  */
-	  else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI]))
-	    errstring = N_("ELF file OS ABI invalid");
-	  else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI],
-					  ehdr->e_ident[EI_ABIVERSION]))
-	    errstring = N_("ELF file ABI version invalid");
-	  else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD],
-			   EI_NIDENT - EI_PAD) != 0)
-	    errstring = N_("nonzero padding in e_ident");
-	  else
-	    /* Otherwise we don't know what went wrong.  */
-	    errstring = N_("internal error");
-
-	  goto lose;
-	}
-
-      if (__glibc_unlikely (ehdr->e_version != EV_CURRENT))
-	{
-	  errstring = N_("ELF file version does not match current one");
-	  goto lose;
-	}
-      if (! __glibc_likely (elf_machine_matches_host (ehdr)))
-	{
-	  __close_nocancel (fd);
-	  __set_errno (ENOENT);
-	  return -1;
-	}
-      else if (__glibc_unlikely (ehdr->e_type != ET_DYN
-				 && ehdr->e_type != ET_EXEC))
-	{
-	  errstring = N_("only ET_DYN and ET_EXEC can be loaded");
-	  goto lose;
-	}
-      else if (__glibc_unlikely (ehdr->e_phentsize != sizeof (ElfW(Phdr))))
-	{
-	  errstring = N_("ELF file's phentsize not the expected size");
-	  goto lose;
-	}
-
-      maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
-      if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
-	phdr = (void *) (fbp->buf + ehdr->e_phoff);
-      else
-	{
-	  phdr = alloca (maplength);
-	  if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
-					   ehdr->e_phoff) != maplength)
-	    {
-	      errval = errno;
-	      errstring = N_("cannot read file data");
-	      goto lose;
-	    }
-	}
-
-      if (__glibc_unlikely (elf_machine_reject_phdr_p
-			    (phdr, ehdr->e_phnum, fbp->buf, fbp->len,
-			     loader, fd)))
-	{
-	  __close_nocancel (fd);
-	  __set_errno (ENOENT);
-	  return -1;
-	}
-
+      int err = do_open_verify (name, fd, fbp, loader,
+                                found_other_class,
+                                free_name);
+      if (err)
+        {
+          __close_nocancel (fd);
+          return -1;
+        }
     }
 
   return fd;
 }
-\f
+
 /* Try to open NAME in one of the directories in *DIRSP.
    Return the fd, or -1.  If successful, fill in *REALNAME
    with the malloc'd full directory name.  If it turns out
-- 
2.37.2


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

* [PATCH 03/12] elf: split _dl_check_loaded() from _dl_map_object
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
  2023-04-03  9:04 ` [PATCH 01/12] elf: split _dl_map_object_from_fd() into reusable parts Stas Sergeev
  2023-04-03  9:04 ` [PATCH 02/12] elf: split open_verify() " Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 04/12] elf: load elf hdr fully in open_verify() Stas Sergeev
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This is a purely mechanical split of a reusable code part.
No functional changes.

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c | 39 +++++++++++++++++++++++++--------------
 1 file changed, 25 insertions(+), 14 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 7a939cb8c1..e05a2a41de 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1988,23 +1988,11 @@ open_path (const char *name, size_t namelen, int mode,
   return -1;
 }
 
-/* Map in the shared object file NAME.  */
-
-struct link_map *
-_dl_map_object (struct link_map *loader, const char *name,
-		int type, int trace_mode, int mode, Lmid_t nsid)
+static struct link_map *
+_dl_check_loaded(const char *name, Lmid_t nsid)
 {
-  int fd;
-  const char *origname = NULL;
-  char *realname;
-  char *name_copy;
   struct link_map *l;
-  struct filebuf fb;
-
-  assert (nsid >= 0);
-  assert (nsid < GL(dl_nns));
 
-  /* Look for this name among those already loaded.  */
   for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
     {
       /* If the requested name matches the soname of a loaded object,
@@ -2033,6 +2021,29 @@ _dl_map_object (struct link_map *loader, const char *name,
       /* We have a match.  */
       return l;
     }
+  return NULL;
+}
+
+/* Map in the shared object file NAME.  */
+
+struct link_map *
+_dl_map_object (struct link_map *loader, const char *name,
+		int type, int trace_mode, int mode, Lmid_t nsid)
+{
+  int fd;
+  const char *origname = NULL;
+  char *realname;
+  char *name_copy;
+  struct link_map *l;
+  struct filebuf fb;
+
+  assert (nsid >= 0);
+  assert (nsid < GL(dl_nns));
+
+  /* Look for this name among those already loaded.  */
+  l = _dl_check_loaded (name, nsid);
+  if (l)
+    return l;
 
   /* Display information if we are debugging.  */
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)
-- 
2.37.2


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

* [PATCH 04/12] elf: load elf hdr fully in open_verify()
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (2 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 03/12] elf: split _dl_check_loaded() from _dl_map_object Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 05/12] elf: switch _dl_map_segment() to anonymous mapping Stas Sergeev
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

open_verify() reads an elf header, putting only the ehdr part into
the filebuf, and reading the rest of the header into the alloca()
space. This requires _ld_map_object_1() (former
_ld_map_object_from_fd()) to read parts of an elf header again,
getting only ehdr from filebuf.
This patch makes filebuf sizeable and reads an entire elf header
there, avoiding the code duplication and getting rid of file reads
in _ld_map_object_1().

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c | 122 +++++++++++++++++++++++++++-----------------------
 1 file changed, 66 insertions(+), 56 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index e05a2a41de..2099a09c80 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -55,7 +55,8 @@ struct filebuf
 #else
 # define FILEBUF_SIZE 832
 #endif
-  char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
+  ssize_t allocated;
+  char *buf;
 };
 
 #include "dynamic-link.h"
@@ -124,6 +125,29 @@ static const size_t system_dirs_len[] =
 };
 #define nsystem_dirs_len array_length (system_dirs_len)
 
+static void
+filebuf_done (struct filebuf *fb)
+{
+  free (fb->buf);
+  fb->buf = NULL;
+  fb->allocated = 0;
+}
+
+static bool
+filebuf_ensure (struct filebuf *fb, size_t size)
+{
+  bool ret = false;
+
+  if (size > fb->allocated)
+    {
+      size_t new_len = size + FILEBUF_SIZE;
+      fb->buf = realloc (fb->buf, new_len);
+      fb->allocated = new_len;
+      ret = true;
+    }
+  return ret;
+}
+
 static bool
 is_trusted_path_normalize (const char *path, size_t len)
 {
@@ -976,18 +1000,8 @@ _dl_map_object_1 (struct link_map *l, int fd,
   l->l_phnum = header->e_phnum;
 
   maplength = header->e_phnum * sizeof (ElfW(Phdr));
-  if (header->e_phoff + maplength <= (size_t) fbp->len)
-    phdr = (void *) (fbp->buf + header->e_phoff);
-  else
-    {
-      phdr = alloca (maplength);
-      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
-				       header->e_phoff) != maplength)
-	{
-	  errstring = N_("cannot read file data");
-	  goto lose_errno;
-	}
-    }
+  assert (header->e_phoff + maplength <= (size_t) fbp->len);
+  phdr = (void *) (fbp->buf + header->e_phoff);
 
    /* On most platforms presume that PT_GNU_STACK is absent and the stack is
     * executable.  Other platforms default to a nonexecutable stack and don't
@@ -1627,31 +1641,16 @@ do_open_verify (const char *name, int fd,
   /* Initialize it to make the compiler happy.  */
   const char *errstring = NULL;
   int errval = 0;
-  ElfW(Ehdr) *ehdr;
+  ElfW(Ehdr) _ehdr;
+  ElfW(Ehdr) *ehdr = &_ehdr;
   ElfW(Phdr) *phdr;
   size_t maplength;
 
   /* We successfully opened the file.  Now verify it is a file
    we can use.  */
   __set_errno (0);
-  fbp->len = 0;
-  assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
   /* Read in the header.  */
-  do
-    {
-      ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len,
-					sizeof (fbp->buf) - fbp->len);
-      if (retlen <= 0)
-        break;
-      fbp->len += retlen;
-    }
-  while (__glibc_unlikely (fbp->len < sizeof (ElfW(Ehdr))));
-
-  /* This is where the ELF header is loaded.  */
-  ehdr = (ElfW(Ehdr) *) fbp->buf;
-
-  /* Now run the tests.  */
-  if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr))))
+  if (__pread64_nocancel (fd, &_ehdr, sizeof(_ehdr), 0) != sizeof(_ehdr))
     {
       errval = errno;
       errstring = (errval == 0
@@ -1752,19 +1751,17 @@ do_open_verify (const char *name, int fd,
     }
 
   maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
-  if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
-    phdr = (void *) (fbp->buf + ehdr->e_phoff);
-  else
+  filebuf_ensure (fbp, maplength + ehdr->e_phoff);
+  if ((size_t) __pread64_nocancel (fd, fbp->buf, maplength +
+				   ehdr->e_phoff, 0) != maplength +
+				   ehdr->e_phoff)
     {
-      phdr = alloca (maplength);
-      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
-					   ehdr->e_phoff) != maplength)
-        {
-          errval = errno;
-          errstring = N_("cannot read file data");
-          goto lose;
-        }
+      errval = errno;
+      errstring = N_("cannot read file data");
+      goto lose;
     }
+  fbp->len = maplength + ehdr->e_phoff;
+  phdr = (void *) (fbp->buf + ehdr->e_phoff);
 
   if (__glibc_unlikely (elf_machine_reject_phdr_p
 		       (phdr, ehdr->e_phnum, fbp->buf, fbp->len,
@@ -2026,16 +2023,16 @@ _dl_check_loaded(const char *name, Lmid_t nsid)
 
 /* Map in the shared object file NAME.  */
 
-struct link_map *
-_dl_map_object (struct link_map *loader, const char *name,
-		int type, int trace_mode, int mode, Lmid_t nsid)
+static struct link_map *
+___dl_map_object (struct link_map *loader, const char *name,
+		  int type, int trace_mode, int mode, Lmid_t nsid,
+		  struct filebuf *fbp)
 {
   int fd;
   const char *origname = NULL;
   char *realname;
   char *name_copy;
   struct link_map *l;
-  struct filebuf fb;
 
   assert (nsid >= 0);
   assert (nsid < GL(dl_nns));
@@ -2100,7 +2097,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	      {
 		fd = open_path (name, namelen, mode,
 				&l->l_rpath_dirs,
-				&realname, &fb, loader, LA_SER_RUNPATH,
+				&realname, fbp, loader, LA_SER_RUNPATH,
 				&found_other_class);
 		if (fd != -1)
 		  break;
@@ -2116,7 +2113,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 			      "RPATH"))
 	    fd = open_path (name, namelen, mode,
 			    &main_map->l_rpath_dirs,
-			    &realname, &fb, loader ?: main_map, LA_SER_RUNPATH,
+			    &realname, fbp, loader ?: main_map, LA_SER_RUNPATH,
 			    &found_other_class);
 
 	  /* Also try DT_RUNPATH in the executable for LD_AUDIT dlopen
@@ -2130,7 +2127,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	      if (cache_rpath (main_map, &l_rpath_dirs,
 			       DT_RUNPATH, "RUNPATH"))
 		fd = open_path (name, namelen, mode, &l_rpath_dirs,
-				&realname, &fb, loader ?: main_map,
+				&realname, fbp, loader ?: main_map,
 				LA_SER_RUNPATH, &found_other_class);
 	    }
 	}
@@ -2138,7 +2135,7 @@ _dl_map_object (struct link_map *loader, const char *name,
       /* Try the LD_LIBRARY_PATH environment variable.  */
       if (fd == -1 && __rtld_env_path_list.dirs != (void *) -1)
 	fd = open_path (name, namelen, mode, &__rtld_env_path_list,
-			&realname, &fb,
+			&realname, fbp,
 			loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
 			LA_SER_LIBPATH, &found_other_class);
 
@@ -2147,7 +2144,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	  && cache_rpath (loader, &loader->l_runpath_dirs,
 			  DT_RUNPATH, "RUNPATH"))
 	fd = open_path (name, namelen, mode,
-			&loader->l_runpath_dirs, &realname, &fb, loader,
+			&loader->l_runpath_dirs, &realname, fbp, loader,
 			LA_SER_RUNPATH, &found_other_class);
 
       if (fd == -1)
@@ -2156,7 +2153,7 @@ _dl_map_object (struct link_map *loader, const char *name,
           if (realname != NULL)
             {
               fd = open_verify (realname, fd,
-                                &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+                                fbp, loader ?: GL(dl_ns)[nsid]._ns_loaded,
                                 LA_SER_CONFIG, mode, &found_other_class,
                                 false);
               if (fd == -1)
@@ -2210,7 +2207,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	      if (cached != NULL)
 		{
 		  fd = open_verify (cached, -1,
-				    &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+				    fbp, loader ?: GL(dl_ns)[nsid]._ns_loaded,
 				    LA_SER_CONFIG, mode, &found_other_class,
 				    false);
 		  if (__glibc_likely (fd != -1))
@@ -2228,7 +2225,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	      || __glibc_likely (!(l->l_flags_1 & DF_1_NODEFLIB)))
 	  && __rtld_search_dirs.dirs != (void *) -1)
 	fd = open_path (name, namelen, mode, &__rtld_search_dirs,
-			&realname, &fb, l, LA_SER_DEFAULT, &found_other_class);
+			&realname, fbp, l, LA_SER_DEFAULT, &found_other_class);
 
       /* Add another newline when we are tracing the library loading.  */
       if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
@@ -2244,7 +2241,7 @@ _dl_map_object (struct link_map *loader, const char *name,
 	fd = -1;
       else
 	{
-	  fd = open_verify (realname, -1, &fb,
+	  fd = open_verify (realname, -1, fbp,
 			    loader ?: GL(dl_ns)[nsid]._ns_loaded, 0, mode,
 			    &found_other_class, true);
 	  if (__glibc_unlikely (fd == -1))
@@ -2305,10 +2302,23 @@ _dl_map_object (struct link_map *loader, const char *name,
     }
 
   void *stack_end = __libc_stack_end;
-  return _dl_map_object_from_fd (name, origname, fd, &fb, realname, loader,
+  return _dl_map_object_from_fd (name, origname, fd, fbp, realname, loader,
 				 type, mode, &stack_end, nsid);
 }
 
+struct link_map *
+_dl_map_object (struct link_map *loader, const char *name,
+                 int type, int trace_mode,
+                 int mode, Lmid_t nsid)
+{
+  struct link_map *ret;
+  struct filebuf fb = {};
+
+  ret = ___dl_map_object (loader, name, type, trace_mode, mode, nsid, &fb);
+  filebuf_done (&fb);
+  return ret;
+}
+
 struct add_path_state
 {
   bool counting;
-- 
2.37.2


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

* [PATCH 05/12] elf: switch _dl_map_segment() to anonymous mapping
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (3 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 04/12] elf: load elf hdr fully in open_verify() Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 06/12] elf: convert pread64 to callback in do_open_verify() Stas Sergeev
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

_dl_map_segment() was mapping entire file image and then was skipping
the load of the first segment. Switch _dl_map_segment() to anonymous
mapping and do not skip the map of the first segment.

Use PROT_READ|PROT_WRITE as a protection. _dl_map_segments() later
sets the proper protection for both file-mapped and anonymous parts.

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-map-segments.h | 69 ++++++++++++++++++++++---------------------
 1 file changed, 35 insertions(+), 34 deletions(-)

diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 504cfc0a41..ed7675cabf 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -22,18 +22,22 @@
 /* Map a segment and align it properly.  */
 
 static __always_inline ElfW(Addr)
-_dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref,
-		 const size_t maplength, int fd)
+_dl_map_segment (ElfW(Addr) mappref, size_t maplength, size_t mapalign)
 {
-  if (__glibc_likely (c->mapalign <= GLRO(dl_pagesize)))
-    return (ElfW(Addr)) __mmap ((void *) mappref, maplength, c->prot,
-				MAP_COPY|MAP_FILE, fd, c->mapoff);
+  int err;
+  /* MAP_COPY is a special flag combination for solibs. */
+  unsigned int map_flags = MAP_ANONYMOUS | MAP_COPY;
+  unsigned int prot = PROT_READ | PROT_WRITE;
+
+  if (__glibc_likely (mapalign <= GLRO(dl_pagesize)))
+    return (ElfW(Addr)) __mmap ((void *) mappref, maplength, prot,
+				map_flags, -1, 0);
 
   /* If the segment alignment > the page size, allocate enough space to
      ensure that the segment can be properly aligned.  */
-  ElfW(Addr) maplen = (maplength >= c->mapalign
-		       ? (maplength + c->mapalign)
-		       : (2 * c->mapalign));
+  ElfW(Addr) maplen = (maplength >= mapalign
+		       ? (maplength + mapalign)
+		       : (2 * mapalign));
   ElfW(Addr) map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplen,
 					      PROT_NONE,
 					      MAP_ANONYMOUS|MAP_PRIVATE,
@@ -41,26 +45,24 @@ _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref,
   if (__glibc_unlikely ((void *) map_start == MAP_FAILED))
     return map_start;
 
-  ElfW(Addr) map_start_aligned = ALIGN_UP (map_start, c->mapalign);
-  map_start_aligned = (ElfW(Addr)) __mmap ((void *) map_start_aligned,
-					   maplength, c->prot,
-					   MAP_COPY|MAP_FILE|MAP_FIXED,
-					   fd, c->mapoff);
-  if (__glibc_unlikely ((void *) map_start_aligned == MAP_FAILED))
-    __munmap ((void *) map_start, maplen);
-  else
+  ElfW(Addr) map_start_aligned = ALIGN_UP (map_start, mapalign);
+  err = __mprotect ((void *) map_start_aligned, maplength, prot);
+  if (__glibc_unlikely (err))
     {
-      /* Unmap the unused regions.  */
-      ElfW(Addr) delta = map_start_aligned - map_start;
-      if (delta)
-	__munmap ((void *) map_start, delta);
-      ElfW(Addr) map_end = map_start_aligned + maplength;
-      map_end = ALIGN_UP (map_end, GLRO(dl_pagesize));
-      delta = map_start + maplen - map_end;
-      if (delta)
-	__munmap ((void *) map_end, delta);
+      __munmap ((void *) map_start, maplen);
+      return (ElfW(Addr)) MAP_FAILED;
     }
 
+  /* Unmap the unused regions.  */
+  ElfW(Addr) delta = map_start_aligned - map_start;
+  if (delta)
+    __munmap ((void *) map_start, delta);
+  ElfW(Addr) map_end = map_start_aligned + maplength;
+  map_end = ALIGN_UP (map_end, GLRO(dl_pagesize));
+  delta = map_start + maplen - map_end;
+  if (delta)
+    __munmap ((void *) map_end, delta);
+
   return map_start_aligned;
 }
 
@@ -98,7 +100,7 @@ _dl_map_segments (struct link_map *l, int fd,
            - MAP_BASE_ADDR (l));
 
       /* Remember which part of the address space this object uses.  */
-      l->l_map_start = _dl_map_segment (c, mappref, maplength, fd);
+      l->l_map_start = _dl_map_segment (mappref, maplength, c->mapalign);
       if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
         return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
 
@@ -123,14 +125,14 @@ _dl_map_segments (struct link_map *l, int fd,
         }
 
       l->l_contiguous = 1;
-
-      goto postmap;
     }
-
-  /* Remember which part of the address space this object uses.  */
-  l->l_map_start = c->mapstart + l->l_addr;
-  l->l_map_end = l->l_map_start + maplength;
-  l->l_contiguous = !has_holes;
+  else
+    {
+      /* Remember which part of the address space this object uses.  */
+      l->l_map_start = c->mapstart + l->l_addr;
+      l->l_map_end = l->l_map_start + maplength;
+      l->l_contiguous = !has_holes;
+    }
 
   while (c < &loadcmds[nloadcmds])
     {
@@ -143,7 +145,6 @@ _dl_map_segments (struct link_map *l, int fd,
               == MAP_FAILED))
         return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
 
-    postmap:
       _dl_postprocess_loadcmd (l, header, c);
 
       if (c->allocend > c->dataend)
-- 
2.37.2


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

* [PATCH 06/12] elf: convert pread64 to callback in do_open_verify()
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (4 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 05/12] elf: switch _dl_map_segment() to anonymous mapping Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 07/12] elf: convert _dl_map_segments's mmap() to a callback Stas Sergeev
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This unbinds do_open_verify() from fd.

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 2099a09c80..ceafaa4228 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1611,10 +1611,18 @@ print_search_path (struct r_search_path_elem **list,
     _dl_debug_printf_c ("\t\t(%s)\n", what);
 }
 
+static ssize_t
+do_pread (void *arg, void *buf, size_t count, off_t offset)
+{
+  int fd = *(const int *) arg;
+  return __pread64_nocancel (fd, buf, count, offset);
+}
+
 static int
-do_open_verify (const char *name, int fd,
+do_open_verify (const char *name, void *fd,
                 struct filebuf *fbp, struct link_map *loader,
-                bool *found_other_class, bool free_name)
+                bool *found_other_class, bool free_name,
+                __typeof (do_pread) *pread_cb)
 {
   /* This is the expected ELF header.  */
 #define ELF32_CLASS ELFCLASS32
@@ -1650,7 +1658,7 @@ do_open_verify (const char *name, int fd,
    we can use.  */
   __set_errno (0);
   /* Read in the header.  */
-  if (__pread64_nocancel (fd, &_ehdr, sizeof(_ehdr), 0) != sizeof(_ehdr))
+  if (pread_cb (fd, &_ehdr, sizeof(_ehdr), 0) != sizeof(_ehdr))
     {
       errval = errno;
       errstring = (errval == 0
@@ -1752,7 +1760,7 @@ do_open_verify (const char *name, int fd,
 
   maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
   filebuf_ensure (fbp, maplength + ehdr->e_phoff);
-  if ((size_t) __pread64_nocancel (fd, fbp->buf, maplength +
+  if ((size_t) pread_cb (fd, fbp->buf, maplength +
 				   ehdr->e_phoff, 0) != maplength +
 				   ehdr->e_phoff)
     {
@@ -1813,9 +1821,9 @@ open_verify (const char *name, int fd,
 
   if (fd != -1)
     {
-      int err = do_open_verify (name, fd, fbp, loader,
+      int err = do_open_verify (name, &fd, fbp, loader,
                                 found_other_class,
-                                free_name);
+                                free_name, do_pread);
       if (err)
         {
           __close_nocancel (fd);
-- 
2.37.2


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

* [PATCH 07/12] elf: convert _dl_map_segments's mmap() to a callback
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (5 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 06/12] elf: convert pread64 to callback in do_open_verify() Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 08/12] elf: call _dl_map_segment() via premap callback Stas Sergeev
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This only applies to file-based mmap.
Since _dl_map_segment() was converted to
anonymous mmap, only 1 place is now converted
to a callback.

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c         | 19 ++++++++++++++-----
 elf/dl-load.h         |  8 ++++++--
 elf/dl-map-segments.h |  6 +++---
 3 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index ceafaa4228..c0704dc89c 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -975,11 +975,12 @@ _dl_process_phdrs (struct link_map *l, int fd)
 }
 
 static int
-_dl_map_object_1 (struct link_map *l, int fd,
+_dl_map_object_1 (struct link_map *l, void *fd,
                   struct filebuf *fbp,
                   int mode, struct link_map *loader,
                   void **stack_endp, int *errval_p,
-                  const char **errstring_p)
+                  const char **errstring_p,
+                  __typeof (do_mmap) *m_map)
 {
   const ElfW(Ehdr) *header;
   const ElfW(Phdr) *phdr;
@@ -1179,7 +1180,7 @@ _dl_map_object_1 (struct link_map *l, int fd,
        l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr
      */
     errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds,
-				  maplength, has_holes, loader);
+				  maplength, has_holes, loader, m_map);
     if (__glibc_unlikely (errstring != NULL))
       {
 	/* Mappings can be in an inconsistent state: avoid unmap.  */
@@ -1430,6 +1431,14 @@ _dl_map_object_2 (struct link_map *l, int mode,
 /* Map in the shared object NAME, actually located in REALNAME, and already
    opened on FD.  */
 
+static void *
+do_mmap (void *addr, size_t length, int prot, int flags,
+         void *arg, off_t offset)
+{
+  int fd = arg ? *(const int *) arg : -1;
+  return __mmap (addr, length, prot, flags, fd, offset);
+}
+
 #ifndef EXTERNAL_MAP_FROM_FD
 static
 #endif
@@ -1550,8 +1559,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
       goto lose_errno;
     }
 
-  if (_dl_map_object_1 (l, fd, fbp, mode, loader, stack_endp, &errval,
-                        &errstring))
+  if (_dl_map_object_1 (l, &fd, fbp, mode, loader, stack_endp, &errval,
+                        &errstring, do_mmap))
     goto lose;
 
   _dl_process_phdrs (l, fd);
diff --git a/elf/dl-load.h b/elf/dl-load.h
index ecf6910c68..eff5146acd 100644
--- a/elf/dl-load.h
+++ b/elf/dl-load.h
@@ -80,6 +80,9 @@ struct loadcmd
   int prot;                             /* PROT_* bits.  */
 };
 
+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_segments.  It should be called for each
    load command, some time after L->l_addr has been set correctly.  It is
@@ -113,13 +116,14 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
    The file <dl-map-segments.h> defines this function.  The canonical
    implementation in elf/dl-map-segments.h might be replaced by a sysdeps
    version.  */
-static const char *_dl_map_segments (struct link_map *l, int fd,
+static const char *_dl_map_segments (struct link_map *l, void *fd,
                                      const ElfW(Ehdr) *header, int type,
                                      const struct loadcmd loadcmds[],
                                      size_t nloadcmds,
                                      const size_t maplength,
                                      bool has_holes,
-                                     struct link_map *loader);
+                                     struct link_map *loader,
+                                     __typeof (do_mmap) *m_map);
 
 /* All the error message strings _dl_map_segments might return are
    listed here so that different implementations in different sysdeps
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index ed7675cabf..7bcfcd9eff 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -74,11 +74,11 @@ _dl_map_segment (ElfW(Addr) mappref, size_t maplength, size_t mapalign)
    other use of those parts of the address space).  */
 
 static __always_inline const char *
-_dl_map_segments (struct link_map *l, int fd,
+_dl_map_segments (struct link_map *l, void *fd,
                   const ElfW(Ehdr) *header, int type,
                   const struct loadcmd loadcmds[], size_t nloadcmds,
                   const size_t maplength, bool has_holes,
-                  struct link_map *loader)
+                  struct link_map *loader, __typeof (do_mmap) *m_map)
 {
   const struct loadcmd *c = loadcmds;
 
@@ -138,7 +138,7 @@ _dl_map_segments (struct link_map *l, int fd,
     {
       if (c->mapend > c->mapstart
           /* Map the segment contents from the file.  */
-          && (__mmap ((void *) (l->l_addr + c->mapstart),
+          && (m_map ((void *) (l->l_addr + c->mapstart),
                       c->mapend - c->mapstart, c->prot,
                       MAP_FIXED|MAP_COPY|MAP_FILE,
                       fd, c->mapoff)
-- 
2.37.2


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

* [PATCH 08/12] elf: call _dl_map_segment() via premap callback
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (6 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 07/12] elf: convert _dl_map_segments's mmap() to a callback Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 09/12] elf: convert _dl_map_object to a callback Stas Sergeev
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This allows to install custom premap callbacks in the subsequent
patches.

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c         | 13 ++++++++++---
 elf/dl-load.h         |  6 +++++-
 elf/dl-map-segments.h |  7 +++++--
 3 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index c0704dc89c..59be8080dd 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -980,7 +980,8 @@ _dl_map_object_1 (struct link_map *l, void *fd,
                   int mode, struct link_map *loader,
                   void **stack_endp, int *errval_p,
                   const char **errstring_p,
-                  __typeof (do_mmap) *m_map)
+                  __typeof (do_mmap) *m_map,
+                  dl_premap_t *premap)
 {
   const ElfW(Ehdr) *header;
   const ElfW(Phdr) *phdr;
@@ -1180,7 +1181,7 @@ _dl_map_object_1 (struct link_map *l, void *fd,
        l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr
      */
     errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds,
-				  maplength, has_holes, loader, m_map);
+				  maplength, has_holes, loader, m_map, premap);
     if (__glibc_unlikely (errstring != NULL))
       {
 	/* Mappings can be in an inconsistent state: avoid unmap.  */
@@ -1439,6 +1440,12 @@ do_mmap (void *addr, size_t length, int prot, int flags,
   return __mmap (addr, length, prot, flags, fd, offset);
 }
 
+static void *
+do_map_segment (void *mappref, size_t maplength, size_t mapalign, void *cookie)
+{
+  return (void *) _dl_map_segment ((ElfW(Addr)) mappref, maplength, mapalign);
+}
+
 #ifndef EXTERNAL_MAP_FROM_FD
 static
 #endif
@@ -1560,7 +1567,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
     }
 
   if (_dl_map_object_1 (l, &fd, fbp, mode, loader, stack_endp, &errval,
-                        &errstring, do_mmap))
+                        &errstring, do_mmap, do_map_segment))
     goto lose;
 
   _dl_process_phdrs (l, fd);
diff --git a/elf/dl-load.h b/elf/dl-load.h
index eff5146acd..e777da5838 100644
--- a/elf/dl-load.h
+++ b/elf/dl-load.h
@@ -84,6 +84,9 @@ static void *
 do_mmap (void *addr, size_t length, int prot, int flags,
          void *arg, off_t offset);
 
+typedef void *
+(dl_premap_t) (void *mappref, size_t maplength, size_t mapalign, void *cookie);
+
 /* This is a subroutine of _dl_map_segments.  It should be called for each
    load command, some time after L->l_addr has been set correctly.  It is
    responsible for setting up the l_text_end and l_phdr fields.  */
@@ -123,7 +126,8 @@ static const char *_dl_map_segments (struct link_map *l, void *fd,
                                      const size_t maplength,
                                      bool has_holes,
                                      struct link_map *loader,
-                                     __typeof (do_mmap) *m_map);
+                                     __typeof (do_mmap) *m_map,
+                                     dl_premap_t *premap);
 
 /* All the error message strings _dl_map_segments might return are
    listed here so that different implementations in different sysdeps
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 7bcfcd9eff..7e3c3b6d53 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -78,7 +78,8 @@ _dl_map_segments (struct link_map *l, void *fd,
                   const ElfW(Ehdr) *header, int type,
                   const struct loadcmd loadcmds[], size_t nloadcmds,
                   const size_t maplength, bool has_holes,
-                  struct link_map *loader, __typeof (do_mmap) *m_map)
+                  struct link_map *loader, __typeof (do_mmap) *m_map,
+                  dl_premap_t *premap)
 {
   const struct loadcmd *c = loadcmds;
 
@@ -100,7 +101,9 @@ _dl_map_segments (struct link_map *l, void *fd,
            - MAP_BASE_ADDR (l));
 
       /* Remember which part of the address space this object uses.  */
-      l->l_map_start = _dl_map_segment (mappref, maplength, c->mapalign);
+      l->l_map_start = (ElfW(Addr)) premap ((void *) mappref,
+                                            maplength, c->mapalign,
+                                            fd);
       if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
         return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
 
-- 
2.37.2


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

* [PATCH 09/12] elf: convert _dl_map_object to a callback
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (7 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 08/12] elf: call _dl_map_segment() via premap callback Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 10/12] dlfcn,elf: implement dlmem() [BZ #11767] Stas Sergeev
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

Subsequent patches will add _dl_map_object_from_memory().

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c | 12 ++++++++++--
 elf/dl-main.h |  9 +++++++++
 elf/dl-open.c | 25 +++++++++++++++++++++----
 3 files changed, 40 insertions(+), 6 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 59be8080dd..6708210da7 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2331,8 +2331,8 @@ ___dl_map_object (struct link_map *loader, const char *name,
 }
 
 struct link_map *
-_dl_map_object (struct link_map *loader, const char *name,
-                 int type, int trace_mode,
+__dl_map_object (struct link_map *loader, const char *name,
+                 void *private, int type, int trace_mode,
                  int mode, Lmid_t nsid)
 {
   struct link_map *ret;
@@ -2343,6 +2343,14 @@ _dl_map_object (struct link_map *loader, const char *name,
   return ret;
 }
 
+struct link_map *
+_dl_map_object (struct link_map *loader, const char *name,
+                int type, int trace_mode,
+                int mode, Lmid_t nsid)
+{
+  return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid);
+}
+
 struct add_path_state
 {
   bool counting;
diff --git a/elf/dl-main.h b/elf/dl-main.h
index 92766d06b4..344a87d5e8 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -104,6 +104,15 @@ struct dl_main_state
   bool version_info;
 };
 
+/* Open the shared object NAME and map in its segments.
+   LOADER's DT_RPATH is used in searching for NAME.
+   If the object is already opened, returns its existing map.  */
+extern struct link_map *
+__dl_map_object (struct link_map *loader,
+                 const char *name, void *private,
+                 int type, int trace_mode, int mode,
+                 Lmid_t nsid) attribute_hidden;
+
 /* Helper function to invoke _dl_init_paths with the right arguments
    from *STATE.  */
 static inline void
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 2d985e21d8..9055b95f17 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -40,6 +40,7 @@
 
 #include <dl-dst.h>
 #include <dl-prop.h>
+#include <dl-main.h>
 
 
 /* We must be careful not to leave us in an inconsistent state.  Thus we
@@ -48,6 +49,7 @@
 struct dl_open_args
 {
   const char *file;
+  void *private;
   int mode;
   /* This is the caller of the dlopen() function.  */
   const void *caller_dlopen;
@@ -55,6 +57,10 @@ struct dl_open_args
   /* Namespace ID.  */
   Lmid_t nsid;
 
+  struct link_map *
+  (*dl_map) (struct link_map *loader, const char *name, void *private,
+             int type, int trace_mode, int mode, Lmid_t nsid);
+
   /* Original value of _ns_global_scope_pending_adds.  Set by
      dl_open_worker.  Only valid if nsid is a real namespace
      (non-negative).  */
@@ -531,7 +537,7 @@ dl_open_worker_begin (void *a)
 
   /* Load the named object.  */
   struct link_map *new;
-  args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,
+  args->map = new = args->dl_map (call_map, file, args->private, lt_loaded, 0,
 				    mode | __RTLD_CALLMAP, args->nsid);
 
   /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is
@@ -820,9 +826,11 @@ dl_open_worker (void *a)
 		      new->l_name, new->l_ns, new->l_direct_opencount);
 }
 
-void *
-_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
-	  int argc, char *argv[], char *env[])
+static void *
+do_dl_open (const char *file, void *private, int mode,
+            const void *caller_dlopen, Lmid_t nsid,
+            int argc, char *argv[], char *env[],
+            __typeof (__dl_map_object) *dl_map)
 {
   if ((mode & RTLD_BINDING_MASK) == 0)
     /* One of the flags must be set.  */
@@ -872,10 +880,12 @@ no more namespaces available for dlmopen()"));
 
   struct dl_open_args args;
   args.file = file;
+  args.private = private;
   args.mode = mode;
   args.caller_dlopen = caller_dlopen;
   args.map = NULL;
   args.nsid = nsid;
+  args.dl_map = dl_map;
   /* args.libc_already_loaded is always assigned by dl_open_worker
      (before any explicit/non-local returns).  */
   args.argc = argc;
@@ -939,6 +949,13 @@ no more namespaces available for dlmopen()"));
   return args.map;
 }
 
+void *
+_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
+          int argc, char *argv[], char *env[])
+{
+  return do_dl_open (file, NULL, mode, caller_dlopen, nsid, argc, argv, env,
+                     __dl_map_object);
+}
 
 void
 _dl_show_scope (struct link_map *l, int from)
-- 
2.37.2


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

* [PATCH 10/12] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (8 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 09/12] elf: convert _dl_map_object to a callback Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 11/12] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag Stas Sergeev
  2023-04-03  9:04 ` [PATCH 12/12] dlfcn,elf: impl DLMEM_GENBUF_SRC " Stas Sergeev
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This patch adds the following function:
void *dlmem(const unsigned char *buffer, size_t size, int flags,
            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                                |  10 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlfcn.h                                 |  22 +++
 dlfcn/dlmem.c                                 |  86 +++++++++
 dlfcn/tst-dlmem-extfns.c                      | 175 ++++++++++++++++++
 elf/dl-load.c                                 | 173 +++++++++++++++++
 elf/dl-load.h                                 |   3 +
 elf/dl-main.h                                 |  12 ++
 elf/dl-object.c                               |   5 +-
 elf/dl-open.c                                 |  13 ++
 elf/rtld.c                                    |   1 +
 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, 546 insertions(+), 2 deletions(-)
 create mode 100644 dlfcn/dlmem.c
 create mode 100644 dlfcn/tst-dlmem-extfns.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..55e5f5fcdf 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-extfns
+CPPFLAGS-tst-dlmem-extfns.c += -DBUILDDIR=\"$(objpfx)\"
 endif
 modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
 		defaultmod2 errmsg1mod modatexit modcxaatexit \
@@ -102,6 +104,12 @@ $(objpfx)glrefmain.out: $(objpfx)glrefmain \
 $(objpfx)failtest.out: $(objpfx)failtestmod.so
 
 $(objpfx)tst-dladdr.out: $(objpfx)glreflib1.so
+# Create fancy container file with solib within.
+$(objpfx)glreflib1.img: $(objpfx)glreflib1.so
+	dd if=/dev/urandom bs=512 count=1 >$@
+	cat $^ >>$@
+	dd if=/dev/urandom bs=512 count=1 >>$@
+$(objpfx)tst-dlmem-extfns.out: $(objpfx)glreflib1.so $(objpfx)glreflib1.img
 
 $(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..fe5e5a7d09 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 int 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..d59eb99ec1
--- /dev/null
+++ b/dlfcn/dlmem.c
@@ -0,0 +1,86 @@
+/* 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 (EINVAL, NULL, NULL, _("invalid mode parameter"));
+  if ((uintptr_t) args->buffer & (GLRO(dl_pagesize) - 1))
+    _dl_signal_error (EINVAL, NULL, NULL, _("buffer not aligned"));
+
+  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;
+}
+
+void *
+___dlmem (const unsigned char *buffer, size_t size, int mode,
+	  struct dlmem_args *dlm_args)
+{
+  return dlmem_implementation (buffer, size, mode, dlm_args,
+				 RETURN_ADDRESS (0));
+}
+
+#ifdef SHARED
+versioned_symbol (libc, ___dlmem, dlmem, GLIBC_2_38);
+#else /* !SHARED */
+weak_alias (___dlmem, dlmem)
+static_link_warning (dlmem)
+#endif /* !SHARED */
diff --git a/dlfcn/tst-dlmem-extfns.c b/dlfcn/tst-dlmem-extfns.c
new file mode 100644
index 0000000000..3b98a6e859
--- /dev/null
+++ b/dlfcn/tst-dlmem-extfns.c
@@ -0,0 +1,175 @@
+/* Test for external functions (fdlopen, dlopen_with_offset4) 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 <sys/stat.h>
+#include <support/check.h>
+
+/* Load the shared library from an open fd. */
+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)
+    return NULL;
+  handle = dlmem (addr, len, flags, NULL);
+  munmap (addr, len);
+  return handle;
+}
+
+/* Load the shared library from a container file.
+   file   - file name.
+   offset - solib offset within a container file.
+            Highly recommended to be page-aligned.
+   length - solib file length (not a container file length).
+   flags  - dlopen() flags. */
+void *dlopen_with_offset4 (const char *file, off_t offset, size_t length,
+                           int flags)
+{
+    void *addr;
+    void *handle;
+    int fd;
+    off_t pad_size = (offset & (getpagesize () - 1));
+    off_t aligned_offset = offset - pad_size;
+    size_t map_length = length + pad_size;
+
+    fd = open (file, O_RDONLY);
+    if (fd == -1)
+        return NULL;
+    addr = mmap (NULL, map_length, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
+    close(fd);
+    if (addr == MAP_FAILED)
+        return NULL;
+    if (pad_size)
+      {
+        /* We need to fix alignment by hands. :-(
+           And for that we need a shared mapping. */
+        void *addr2 = mmap (NULL, length, PROT_READ | PROT_WRITE,
+                            MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+        if (addr2 == MAP_FAILED)
+          {
+            munmap (addr, map_length);
+            return NULL;
+          }
+        memcpy (addr2, addr + pad_size, length);
+        munmap (addr, map_length);
+        addr = addr2;
+        map_length = length;
+      }
+    handle = dlmem (addr, length, flags, NULL);
+    munmap (addr, map_length);
+    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;
+  struct stat sb;
+  const unsigned char *unaligned_buf = (const unsigned char *) 1;
+
+  /* First check the reaction to unaligned buf. */
+  handle = dlmem (unaligned_buf, 4096, RTLD_NOW, NULL);
+  TEST_VERIFY (handle == NULL);
+  /* errno is set by dlerror() so needs to print something. */
+  printf ("unaligned buf gives %s\n", dlerror ());
+  TEST_COMPARE (errno, EINVAL);
+
+  fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
+  if (fd == -1)
+    error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
+  fstat (fd, &sb);
+  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");
+
+  dlclose (handle);
+
+  /* Try to load the solib from container, with the worst, unaligned case. */
+  handle = dlopen_with_offset4 (BUILDDIR "glreflib1.img", 512, sb.st_size,
+                                RTLD_NOW);
+
+  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");
+
+  /* Handle can be closed only after checking info, as some info fields
+     point to memory that is freed by dlclose(). */
+  dlclose (handle);
+  return 0;
+}
+
+
+#include <support/test-driver.c>
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 6708210da7..fd81a9103e 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>
@@ -2351,6 +2352,178 @@ _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;
+  size_t to_copy = 0;
+
+  assert (flags & MAP_FIXED);
+  if (offset < fb->len)
+    {
+      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
+    }
+  /* memset the rest. */
+  if (length > to_copy)
+    memset (addr + to_copy, 0, length - to_copy);
+  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 (_dl_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval,
+                        &errstring, do_memremap, do_dlmem_premap))
+    goto lose;
+
+  _dl_map_object_2 (l, mode, id, NULL, nsid);
+  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-object.c b/elf/dl-object.c
index f1f2ec956c..ab926cd4bf 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -122,7 +122,10 @@ _dl_new_object (char *realname, const char *libname, int type,
 #endif
     new->l_name = realname;
   else
-    new->l_name = (char *) newname->name + libname_len - 1;
+    /* When realname="", it is not allocated and points to the constant
+       string. Constness is dropped by an explicit cast. :(
+       So strdup() it here. */
+    new->l_name = __strdup ("");
 
   new->l_type = type;
   /* If we set the bit now since we know it is never used we avoid
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 9055b95f17..bcae45d714 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -957,6 +957,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 c1e383b055..30c3190593 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -367,6 +367,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/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 e1ab272a79..fae485fe1f 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -664,6 +664,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
@@ -1235,6 +1238,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
-- 
2.37.2


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

* [PATCH 11/12] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (9 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 10/12] dlfcn,elf: implement dlmem() [BZ #11767] Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  2023-04-03  9:04 ` [PATCH 12/12] dlfcn,elf: impl DLMEM_GENBUF_SRC " Stas Sergeev
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This flag preserves the destination mapping by using memcpy()
from the source buffer. It is useful if the backing-store was
mapped with MAP_SHARED.

This patch adds a test-case named tst-dlmem-shm. It maps solib
into shm and checks that dlmem with that flag worked as expected,
by resolving the solib symbols. Then it checks the new functionality
of creating the library duplicate, that this flag permits.

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/dlfcn.h         |   4 +
 dlfcn/glreflib1.c     |   2 +
 dlfcn/tst-dlmem-shm.c | 169 ++++++++++++++++++++++++++++++++++++++++++
 elf/dl-load.c         |  36 ++++++++-
 elf/dl-map-segments.h |   4 +-
 6 files changed, 216 insertions(+), 4 deletions(-)
 create mode 100644 dlfcn/tst-dlmem-shm.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 55e5f5fcdf..a71810e941 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -52,8 +52,10 @@ 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 tst-dlmem-extfns
+	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem-extfns \
+	tst-dlmem-shm
 CPPFLAGS-tst-dlmem-extfns.c += -DBUILDDIR=\"$(objpfx)\"
+CPPFLAGS-tst-dlmem-shm.c += -DBUILDDIR=\"$(objpfx)\"
 endif
 modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
 		defaultmod2 errmsg1mod modatexit modcxaatexit \
@@ -110,6 +112,7 @@ $(objpfx)glreflib1.img: $(objpfx)glreflib1.so
 	cat $^ >>$@
 	dd if=/dev/urandom bs=512 count=1 >>$@
 $(objpfx)tst-dlmem-extfns.out: $(objpfx)glreflib1.so $(objpfx)glreflib1.img
+$(objpfx)tst-dlmem-shm.out: $(objpfx)glreflib1.so
 
 $(objpfx)tst-dlinfo.out: $(objpfx)glreflib3.so
 LDFLAGS-glreflib3.so = -Wl,-rpath,:
diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h
index fe5e5a7d09..7aa9d7d3cf 100644
--- a/dlfcn/dlfcn.h
+++ b/dlfcn/dlfcn.h
@@ -73,6 +73,10 @@ typedef void *
 (dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
 	          void *cookie);
 
+/* Do not replace mapping created by premap callback.
+   dlmem() will then use memcpy(). */
+#define DLMEM_DONTREPLACE 1
+
 struct dlmem_args {
   /* Optional name to associate with the loaded object. */
   const char *soname;
diff --git a/dlfcn/glreflib1.c b/dlfcn/glreflib1.c
index f26832fabe..bab3fcd1b0 100644
--- a/dlfcn/glreflib1.c
+++ b/dlfcn/glreflib1.c
@@ -22,3 +22,5 @@ ref1 (void)
 {
   return 42;
 }
+
+int bar = 35;
diff --git a/dlfcn/tst-dlmem-shm.c b/dlfcn/tst-dlmem-shm.c
new file mode 100644
index 0000000000..7899dfc909
--- /dev/null
+++ b/dlfcn/tst-dlmem-shm.c
@@ -0,0 +1,169 @@
+/* Test for dlmem into shm.
+   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 size_t maplen;
+
+static void *
+premap_dlmem (void *mappref, size_t maplength, size_t mapalign, void *cookie)
+{
+  int fd = * (int *) cookie;
+  int prot = PROT_READ | PROT_WRITE;
+  int err;
+
+  /* See if we support such parameters. */
+  if (mappref || mapalign > 4096)
+    return MAP_FAILED;
+
+  fprintf (stderr, "%s\n", __func__);
+
+  err = ftruncate (fd, maplength);
+  if (err)
+    error (EXIT_FAILURE, 0, "ftruncate() failed");
+  maplen = maplength;
+  return mmap (NULL, maplength, prot, MAP_SHARED | MAP_FILE
+#ifdef MAP_32BIT
+                                      | MAP_32BIT
+#endif
+                                      , fd, 0);
+}
+
+#define TEST_FUNCTION do_test
+extern int do_test (void);
+
+int
+do_test (void)
+{
+  void *handle;
+  void *addr;
+  int (*sym) (void); /* We load ref1 from glreflib1.c.  */
+  int *bar, *bar2;
+  unsigned char *addr2;
+  Dl_info info;
+  int ret;
+  int fd;
+  int num;
+  off_t len;
+  struct link_map *lm;
+  const char *shm_name = "/tst-dlmem";
+  int shm_fd;
+  struct dlmem_args a;
+
+  shm_fd = memfd_create (shm_name, 0);
+  if (shm_fd == -1)
+    error (EXIT_FAILURE, 0, "shm_open() failed");
+
+  fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
+  if (fd == -1)
+    error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
+  len = lseek (fd, 0, SEEK_END);
+  lseek (fd, 0, SEEK_SET);
+  addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (addr == MAP_FAILED)
+    error (EXIT_FAILURE, 0, "cannot mmap: glreflib1.so");
+  a.soname = "glreflib1.so";
+  a.flags = DLMEM_DONTREPLACE;
+  a.nsid = LM_ID_BASE;
+  a.premap = premap_dlmem;
+  a.cookie = &shm_fd;
+  handle = dlmem (addr, len, RTLD_NOW | RTLD_LOCAL, &a);
+  if (handle == NULL)
+    error (EXIT_FAILURE, 0, "cannot load: glreflib1.so");
+  munmap (addr, len);
+  close (fd);
+  /* Check if premap was called. */
+  TEST_VERIFY (maplen != 0);
+
+  sym = dlsym (handle, "ref1");
+  if (sym == NULL)
+    error (EXIT_FAILURE, 0, "dlsym failed");
+
+  memset (&info, 0, sizeof (info));
+  ret = dladdr (sym, &info);
+  if (ret == 0)
+    error (EXIT_FAILURE, 0, "dladdr failed");
+#ifdef MAP_32BIT
+  /* Make sure MAP_32BIT worked. */
+  if ((unsigned long) info.dli_fbase >= 0x100000000)
+    error (EXIT_FAILURE, 0, "premap audit didn't work");
+#endif
+  ret = dlinfo (handle, RTLD_DI_LINKMAP, &lm);
+  if (ret != 0)
+    error (EXIT_FAILURE, 0, "dlinfo 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);
+  printf ("lm->l_addr = %lx\n", lm->l_addr);
+
+  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");
+
+  num = sym ();
+  if (num != 42)
+    error (EXIT_FAILURE, 0, "bad return from ref1");
+
+  /* Now try symbol duplication. */
+  bar = dlsym (handle, "bar");
+  if (bar == NULL)
+    error (EXIT_FAILURE, 0, "dlsym failed");
+  TEST_COMPARE (*bar, 35);
+  /* write another value */
+#define TEST_BAR_VAL 48
+  *bar = TEST_BAR_VAL;
+
+  /* Create second instance of the solib. */
+  addr2 = mmap (NULL, maplen, PROT_READ | PROT_WRITE | PROT_EXEC,
+               MAP_SHARED, shm_fd, 0);
+  if (addr2 == MAP_FAILED)
+    error (EXIT_FAILURE, 0, "cannot mmap shm\n");
+  /* Find our bar symbol duplicate. */
+  ret = dladdr (bar, &info);
+  if (ret == 0)
+    error (EXIT_FAILURE, 0, "dladdr failed");
+  bar2 = (int *) (addr2 + (info.dli_saddr - info.dli_fbase));
+  /* See if we found the right one. */
+  TEST_COMPARE (*bar2, TEST_BAR_VAL);
+
+  munmap (addr2, maplen);
+  close (shm_fd);
+  dlclose (handle);
+
+  return 0;
+}
+
+
+#include <support/test-driver.c>
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fd81a9103e..422c03459b 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2352,6 +2352,31 @@ _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_mmapcpy (void *addr, size_t length, int prot, int flags,
+            void *arg, off_t offset)
+{
+  const struct dlmem_fbuf *fb = arg;
+  size_t to_copy = 0;
+
+  assert (flags & MAP_FIXED);
+  assert ((flags & MAP_ANONYMOUS) || fb);
+
+  if (!(flags & MAP_ANONYMOUS) && offset < fb->len)
+    {
+      to_copy = length;
+      if (offset + to_copy > fb->len)
+        to_copy = fb->len - offset;
+      memcpy (addr, fb->buf + offset, to_copy);
+    }
+  /* memset the rest. */
+  if (length > to_copy)
+    memset (addr + to_copy, 0, length - to_copy);
+  if (__mprotect (addr, length, prot) == -1)
+    return MAP_FAILED;
+  return addr;
+}
+
 static void *
 do_memremap (void *addr, size_t length, int prot, int flags,
              void *arg, off_t offset)
@@ -2360,6 +2385,11 @@ do_memremap (void *addr, size_t length, int prot, int flags,
   size_t to_copy = 0;
 
   assert (flags & MAP_FIXED);
+  assert ((flags & MAP_ANONYMOUS) || fb);
+
+  if (flags & MAP_ANONYMOUS)
+    return __mmap (addr, length, prot, flags, -1, 0);
+
   if (offset < fb->len)
     {
       to_copy = length;
@@ -2430,6 +2460,10 @@ ___dl_map_object_from_mem (struct link_map *loader, const char *name,
   struct r_debug *r = _dl_debug_update (nsid);
   bool make_consistent = false;
   struct r_file_id id = {};
+  const struct dlmem_fbuf *fb = private;
+  unsigned dlmem_flags = fb->dlm_args ? fb->dlm_args->flags : 0;
+  __typeof (do_mmap) *m_map = (dlmem_flags & DLMEM_DONTREPLACE)
+                             ? do_mmapcpy : do_memremap;
 
   assert (nsid >= 0);
   assert (nsid < GL(dl_nns));
@@ -2480,7 +2514,7 @@ ___dl_map_object_from_mem (struct link_map *loader, const char *name,
 
   void *stack_end = __libc_stack_end;
   if (_dl_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval,
-                        &errstring, do_memremap, do_dlmem_premap))
+                        &errstring, m_map, do_dlmem_premap))
     goto lose;
 
   _dl_map_object_2 (l, mode, id, NULL, nsid);
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 7e3c3b6d53..3fb9aac6cb 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -187,9 +187,9 @@ _dl_map_segments (struct link_map *l, void *fd,
             {
               /* Map the remaining zero pages in from the zero fill FD.  */
               caddr_t mapat;
-              mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
+              mapat = m_map ((caddr_t) zeropage, zeroend - zeropage,
                               c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
-                              -1, 0);
+                              NULL, 0);
               if (__glibc_unlikely (mapat == MAP_FAILED))
                 return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL;
             }
-- 
2.37.2


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

* [PATCH 12/12] dlfcn,elf: impl DLMEM_GENBUF_SRC dlmem() flag
  2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
                   ` (10 preceding siblings ...)
  2023-04-03  9:04 ` [PATCH 11/12] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag Stas Sergeev
@ 2023-04-03  9:04 ` Stas Sergeev
  11 siblings, 0 replies; 13+ messages in thread
From: Stas Sergeev @ 2023-04-03  9:04 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

This flag allows to use a generic unaligned memory buffer or a
private anonymous mapping as a source. It should not be preferred
over a file-backed mapping when possible, but the "bad" cases also
needs to be supported.

New tests added to tst-dlmem-shm test-case.

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/dlfcn.h         |  4 ++++
 dlfcn/dlmem.c         |  6 +++++-
 dlfcn/tst-dlmem-shm.c | 37 ++++++++++++++++++++++++++++++++++++-
 elf/dl-load.c         |  6 ++++++
 4 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h
index 7aa9d7d3cf..976d93a464 100644
--- a/dlfcn/dlfcn.h
+++ b/dlfcn/dlfcn.h
@@ -76,6 +76,10 @@ typedef void *
 /* Do not replace mapping created by premap callback.
    dlmem() will then use memcpy(). */
 #define DLMEM_DONTREPLACE 1
+/* Treat source memory buffer as a generic unaligned buffer, rather
+   than a file-backed or anonymously-shared mapping. Anonymous private
+   mapping also needs this flag to be set. */
+#define DLMEM_GENBUF_SRC 2
 
 struct dlmem_args {
   /* Optional name to associate with the loaded object. */
diff --git a/dlfcn/dlmem.c b/dlfcn/dlmem.c
index d59eb99ec1..fc8facb6d2 100644
--- a/dlfcn/dlmem.c
+++ b/dlfcn/dlmem.c
@@ -40,12 +40,16 @@ static void
 dlmem_doit (void *a)
 {
   struct _dlmem_args *args = (struct _dlmem_args *) a;
+  const struct dlmem_args *dlm_args = args->args;
 
   if (args->mode & ~(RTLD_BINDING_MASK | RTLD_NOLOAD | RTLD_DEEPBIND
 		     | RTLD_GLOBAL | RTLD_LOCAL | RTLD_NODELETE
 		     | __RTLD_SPROF))
     _dl_signal_error (EINVAL, NULL, NULL, _("invalid mode parameter"));
-  if ((uintptr_t) args->buffer & (GLRO(dl_pagesize) - 1))
+
+  /* Unaligned buffer is only permitted when DLMEM_GENBUF_SRC flag set. */
+  if (((uintptr_t) args->buffer & (GLRO(dl_pagesize) - 1))
+      && (!dlm_args || !(dlm_args->flags & DLMEM_GENBUF_SRC)))
     _dl_signal_error (EINVAL, NULL, NULL, _("buffer not aligned"));
 
   args->new = GLRO(dl_mem) (args->buffer, args->size,
diff --git a/dlfcn/tst-dlmem-shm.c b/dlfcn/tst-dlmem-shm.c
index 7899dfc909..16d13f16d6 100644
--- a/dlfcn/tst-dlmem-shm.c
+++ b/dlfcn/tst-dlmem-shm.c
@@ -70,10 +70,11 @@ do_test (void)
   int fd;
   int num;
   off_t len;
+  off_t orig_len;
   struct link_map *lm;
   const char *shm_name = "/tst-dlmem";
   int shm_fd;
-  struct dlmem_args a;
+  struct dlmem_args a = {};
 
   shm_fd = memfd_create (shm_name, 0);
   if (shm_fd == -1)
@@ -84,9 +85,43 @@ do_test (void)
     error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
   len = lseek (fd, 0, SEEK_END);
   lseek (fd, 0, SEEK_SET);
+  /* For the sake of testing add extra space. */
+  orig_len = len;
+  len += 4096;
   addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
   if (addr == MAP_FAILED)
     error (EXIT_FAILURE, 0, "cannot mmap: glreflib1.so");
+
+  /* Try unaligned buffer. */
+  handle = dlmem (addr + 4, len, RTLD_NOW | RTLD_LOCAL, NULL);
+  TEST_VERIFY (handle == NULL);
+  /* errno is set by dlerror() so needs to print something. */
+  printf ("unaligned buf gives %s\n", dlerror ());
+  TEST_COMPARE (errno, EINVAL);
+  /* Try allow unaligned buffer but not at the beginning of solib. */
+  a.flags = DLMEM_GENBUF_SRC;
+  handle = dlmem (addr + 4, len, RTLD_NOW | RTLD_LOCAL, &a);
+  TEST_VERIFY (handle == NULL);
+  printf ("non-elf data gives %s\n", dlerror ());
+  TEST_COMPARE (errno, EINVAL);
+  /* Try allow unaligned buffer but with good solib. */
+  mprotect (addr, len, PROT_READ | PROT_WRITE);
+  memmove (addr + 4, addr, orig_len);
+  /* Forgot to allow unaligned buffer. */
+  handle = dlmem (addr + 4, len, RTLD_NOW | RTLD_LOCAL, NULL);
+  TEST_VERIFY (handle == NULL);
+  /* Should now be well. */
+  handle = dlmem (addr + 4, len, RTLD_NOW | RTLD_LOCAL, &a);
+  TEST_VERIFY (handle != NULL);
+
+  /* Lets do this all again, now for real. */
+  dlclose (handle);
+  munmap (addr, len);
+  len = orig_len;
+  addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (addr == MAP_FAILED)
+    error (EXIT_FAILURE, 0, "cannot mmap: glreflib1.so");
+
   a.soname = "glreflib1.so";
   a.flags = DLMEM_DONTREPLACE;
   a.nsid = LM_ID_BASE;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 422c03459b..002afbee3f 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2382,6 +2382,7 @@ do_memremap (void *addr, size_t length, int prot, int flags,
              void *arg, off_t offset)
 {
   const struct dlmem_fbuf *fb = arg;
+  const struct dlmem_args *dlm_args = fb->dlm_args;
   size_t to_copy = 0;
 
   assert (flags & MAP_FIXED);
@@ -2390,6 +2391,11 @@ do_memremap (void *addr, size_t length, int prot, int flags,
   if (flags & MAP_ANONYMOUS)
     return __mmap (addr, length, prot, flags, -1, 0);
 
+  /* With DLMEM_GENBUF_SRC flag, everything but anonymous mmaps goes
+     to memcpy. */
+  if (dlm_args && (dlm_args->flags & DLMEM_GENBUF_SRC))
+    return do_mmapcpy(addr, length, prot, flags, arg, offset);
+
   if (offset < fb->len)
     {
       to_copy = length;
-- 
2.37.2


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

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

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-03  9:04 [PATCH v10 0/12] implement dlmem() function Stas Sergeev
2023-04-03  9:04 ` [PATCH 01/12] elf: split _dl_map_object_from_fd() into reusable parts Stas Sergeev
2023-04-03  9:04 ` [PATCH 02/12] elf: split open_verify() " Stas Sergeev
2023-04-03  9:04 ` [PATCH 03/12] elf: split _dl_check_loaded() from _dl_map_object Stas Sergeev
2023-04-03  9:04 ` [PATCH 04/12] elf: load elf hdr fully in open_verify() Stas Sergeev
2023-04-03  9:04 ` [PATCH 05/12] elf: switch _dl_map_segment() to anonymous mapping Stas Sergeev
2023-04-03  9:04 ` [PATCH 06/12] elf: convert pread64 to callback in do_open_verify() Stas Sergeev
2023-04-03  9:04 ` [PATCH 07/12] elf: convert _dl_map_segments's mmap() to a callback Stas Sergeev
2023-04-03  9:04 ` [PATCH 08/12] elf: call _dl_map_segment() via premap callback Stas Sergeev
2023-04-03  9:04 ` [PATCH 09/12] elf: convert _dl_map_object to a callback Stas Sergeev
2023-04-03  9:04 ` [PATCH 10/12] dlfcn,elf: implement dlmem() [BZ #11767] Stas Sergeev
2023-04-03  9:04 ` [PATCH 11/12] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag Stas Sergeev
2023-04-03  9:04 ` [PATCH 12/12] dlfcn,elf: impl DLMEM_GENBUF_SRC " Stas Sergeev

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