public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v9 0/13] implement dlmem() function
@ 2023-03-18 16:50 Stas Sergeev
  2023-03-18 16:50 ` [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
                   ` (14 more replies)
  0 siblings, 15 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:50 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

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] 107+ messages in thread

* [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100]
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
@ 2023-03-18 16:50 ` Stas Sergeev
  2023-03-29 13:54   ` Adhemerval Zanella Netto
  2023-03-18 16:50 ` [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping Stas Sergeev
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:50 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

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

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

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

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

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

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


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

* [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
  2023-03-18 16:50 ` [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
@ 2023-03-18 16:50 ` Stas Sergeev
  2023-03-29 17:01   ` Adhemerval Zanella Netto
  2023-03-18 16:51 ` [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx Stas Sergeev
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:50 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 | 73 +++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 34 deletions(-)

diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 504cfc0a41..9af8cae188 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -22,18 +22,26 @@
 /* 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;
+  unsigned map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
+  unsigned prot = PROT_READ | PROT_WRITE;
+
+#ifdef MAP_DENYWRITE
+  /* Tell mmap() that we are mapping solib. This flag enables things
+     like LD_PREFER_MAP_32BIT_EXEC. */
+  map_flags |= MAP_DENYWRITE;
+#endif
+  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 +49,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 +104,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 +129,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 +149,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] 107+ messages in thread

* [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
  2023-03-18 16:50 ` [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
  2023-03-18 16:50 ` [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-29 17:10   ` Adhemerval Zanella Netto
  2023-03-18 16:51 ` [PATCH 04/13] elf: split _dl_map_object_from_fd() into reusable parts Stas Sergeev
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 UTC (permalink / raw)
  To: libc-alpha; +Cc: Stas Sergeev

It is not used in these functions.
rtld.c:rtld_setup_main_map() does the same.

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 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index fcb39a78d4..ab8b648687 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1379,10 +1379,10 @@ cannot enable executable stack as shared object requires");
     switch (ph[-1].p_type)
       {
       case PT_NOTE:
-	_dl_process_pt_note (l, fd, &ph[-1]);
+	_dl_process_pt_note (l, -1, &ph[-1]);
 	break;
       case PT_GNU_PROPERTY:
-	_dl_process_pt_gnu_property (l, fd, &ph[-1]);
+	_dl_process_pt_gnu_property (l, -1, &ph[-1]);
 	break;
       }
 
-- 
2.37.2


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

* [PATCH 04/13] elf: split _dl_map_object_from_fd() into reusable parts
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (2 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-18 16:51 ` [PATCH 05/13] elf: split open_verify() " Stas Sergeev
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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 | 317 ++++++++++++++++++++++++++++----------------------
 1 file changed, 175 insertions(+), 142 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index ab8b648687..9e0e63b9a3 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -929,147 +929,25 @@ _dl_process_pt_gnu_property (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.  */
-
-#ifndef EXTERNAL_MAP_FROM_FD
-static
-#endif
-struct link_map *
-_dl_map_object_from_fd (const char *name, const char *origname, int fd,
-			struct filebuf *fbp, char *realname,
-			struct link_map *loader, int l_type, int mode,
-			void **stack_endp, Lmid_t nsid)
+static int
+_ld_map_object_1 (struct link_map *l, 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;
-  struct r_debug *r = _dl_debug_update (nsid);
-  bool make_consistent = false;
-
-  /* Get file information.  To match the kernel behavior, do not fill
-     in this information for the executable in case of an explicit
-     loader invocation.  */
-  struct r_file_id id;
-  if (mode & __RTLD_OPENEXEC)
-    {
-      assert (nsid == LM_ID_BASE);
-      memset (&id, 0, sizeof (id));
-    }
-  else
-    {
-      if (__glibc_unlikely (!_dl_get_file_id (fd, &id)))
-	{
-	  errstring = N_("cannot stat shared object");
-	lose_errno:
-	  errval = errno;
-	lose:
-	  /* The file might already be closed.  */
-	  if (fd != -1)
-	    __close_nocancel (fd);
-	  if (l != NULL && l->l_map_start != 0)
-	    _dl_unmap_segments (l);
-	  if (l != NULL && l->l_origin != (char *) -1l)
-	    free ((char *) l->l_origin);
-	  if (l != NULL && !l->l_libname->dont_free)
-	    free (l->l_libname);
-	  if (l != NULL && l->l_phdr_allocated)
-	    free ((void *) l->l_phdr);
-	  free (l);
-	  free (realname);
-
-	  if (make_consistent && r != NULL)
-	    {
-	      r->r_state = RT_CONSISTENT;
-	      _dl_debug_state ();
-	      LIBC_PROBE (map_failed, 2, nsid, r);
-	    }
-
-	  _dl_signal_error (errval, name, NULL, errstring);
-	}
-
-      /* Look again to see if the real name matched another already loaded.  */
-      for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
-	if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
-	  {
-	    /* The object is already loaded.
-	       Just bump its reference count and return it.  */
-	    __close_nocancel (fd);
-
-	    /* If the name is not in the list of names for this object add
-	       it.  */
-	    free (realname);
-	    add_name_to_object (l, name);
-
-	    return l;
-	  }
-    }
-
-#ifdef SHARED
-  /* When loading into a namespace other than the base one we must
-     avoid loading ld.so since there can only be one copy.  Ever.  */
-  if (__glibc_unlikely (nsid != LM_ID_BASE)
-      && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id)
-	  || _dl_name_match_p (name, &GL(dl_rtld_map))))
-    {
-      /* This is indeed ld.so.  Create a new link_map which refers to
-	 the real one for almost everything.  */
-      l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
-      if (l == NULL)
-	goto fail_new;
-
-      /* Refer to the real descriptor.  */
-      l->l_real = &GL(dl_rtld_map);
-
-      /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen().  */
-      l->l_addr = l->l_real->l_addr;
-      l->l_ld = l->l_real->l_ld;
-
-      /* No need to bump the refcount of the real object, ld.so will
-	 never be unloaded.  */
-      __close_nocancel (fd);
-
-      /* Add the map for the mirrored object to the object list.  */
-      _dl_add_to_namespace_list (l, nsid);
-
-      return l;
-    }
-#endif
-
-  if (mode & RTLD_NOLOAD)
-    {
-      /* We are not supposed to load the object unless it is already
-	 loaded.  So return now.  */
-      free (realname);
-      __close_nocancel (fd);
-      return NULL;
-    }
-
-  /* Print debugging message.  */
-  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
-    _dl_debug_printf ("file=%s [%lu];  generating link map\n", name, nsid);
+#define errstring (*errstring_p)
+#define errval (*errval_p)
 
   /* This is the ELF header.  We read it in `open_verify'.  */
   header = (void *) fbp->buf;
 
-  /* Enter the new object in the list of loaded objects.  */
-  l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
-  if (__glibc_unlikely (l == NULL))
-    {
-#ifdef SHARED
-    fail_new:
-#endif
-      errstring = N_("cannot create shared object descriptor");
-      goto lose_errno;
-    }
-
   /* Extract the remaining details we need from the ELF header
      and then read in the program header table.  */
   l->l_entry = header->e_entry;
@@ -1093,7 +971,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.  */
@@ -1386,15 +1264,6 @@ cannot enable executable stack as shared object requires");
 	break;
       }
 
-  /* We are done mapping in the file.  We no longer need the descriptor.  */
-  if (__glibc_unlikely (__close_nocancel (fd) != 0))
-    {
-      errstring = N_("cannot close file descriptor");
-      goto lose_errno;
-    }
-  /* Signal that we closed the file.  */
-  fd = -1;
-
   /* Failures before this point are handled locally via lose.
      There are no more failures in this function until return,
      to change that the cleanup handling needs to be updated.  */
@@ -1419,6 +1288,23 @@ cannot enable executable stack as shared object requires");
 			   (unsigned long int) l->l_phdr,
 			   (int) sizeof (void *) * 2, l->l_phnum);
 
+  return 0;
+
+lose_errno:
+  errval = errno;
+lose:
+  return -1;
+
+#undef errval
+#undef errstring
+}
+
+static void
+_ld_map_object_2 (struct link_map *l, int mode,
+                  struct r_file_id id, const char *origname,
+                  Lmid_t nsid, struct r_debug *r,
+                  bool *make_consistent_p)
+{
   /* Set up the symbol hash table.  */
   _dl_setup_hash (l);
 
@@ -1510,7 +1396,7 @@ cannot enable executable stack as shared object requires");
       r->r_state = RT_ADD;
       _dl_debug_state ();
       LIBC_PROBE (map_start, 2, nsid, r);
-      make_consistent = true;
+      *make_consistent_p = true;
     }
   else
     assert (r->r_state == RT_ADD);
@@ -1520,10 +1406,157 @@ 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;
+  struct r_debug *r = _dl_debug_update (nsid);
+  bool make_consistent = false;
+
+  /* Get file information.  To match the kernel behavior, do not fill
+     in this information for the executable in case of an explicit
+     loader invocation.  */
+  struct r_file_id id;
+  if (mode & __RTLD_OPENEXEC)
+    {
+      assert (nsid == LM_ID_BASE);
+      memset (&id, 0, sizeof (id));
+    }
+  else
+    {
+      if (__glibc_unlikely (!_dl_get_file_id (fd, &id)))
+	{
+	  errstring = N_("cannot stat shared object");
+	lose_errno:
+	  errval = errno;
+	lose:
+	  /* The file might already be closed.  */
+	  if (fd != -1)
+	    __close_nocancel (fd);
+	  if (l != NULL && l->l_map_start != 0)
+	    _dl_unmap_segments (l);
+	  if (l != NULL && l->l_origin != (char *) -1l)
+	    free ((char *) l->l_origin);
+	  if (l != NULL && !l->l_libname->dont_free)
+	    free (l->l_libname);
+	  if (l != NULL && l->l_phdr_allocated)
+	    free ((void *) l->l_phdr);
+	  free (l);
+	  free (realname);
+
+	  if (make_consistent && r != NULL)
+	    {
+	      r->r_state = RT_CONSISTENT;
+	      _dl_debug_state ();
+	      LIBC_PROBE (map_failed, 2, nsid, r);
+	    }
+
+	  _dl_signal_error (errval, name, NULL, errstring);
+	}
+
+      /* Look again to see if the real name matched another already loaded.  */
+      for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
+	if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
+	  {
+	    /* The object is already loaded.
+	       Just bump its reference count and return it.  */
+	    __close_nocancel (fd);
+
+	    /* If the name is not in the list of names for this object add
+	       it.  */
+	    free (realname);
+	    add_name_to_object (l, name);
+
+	    return l;
+	  }
+    }
+
+#ifdef SHARED
+  /* When loading into a namespace other than the base one we must
+     avoid loading ld.so since there can only be one copy.  Ever.  */
+  if (__glibc_unlikely (nsid != LM_ID_BASE)
+      && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id)
+	  || _dl_name_match_p (name, &GL(dl_rtld_map))))
+    {
+      /* This is indeed ld.so.  Create a new link_map which refers to
+	 the real one for almost everything.  */
+      l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
+      if (l == NULL)
+	goto fail_new;
+
+      /* Refer to the real descriptor.  */
+      l->l_real = &GL(dl_rtld_map);
+
+      /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen().  */
+      l->l_addr = l->l_real->l_addr;
+      l->l_ld = l->l_real->l_ld;
+
+      /* No need to bump the refcount of the real object, ld.so will
+	 never be unloaded.  */
+      __close_nocancel (fd);
+
+      /* Add the map for the mirrored object to the object list.  */
+      _dl_add_to_namespace_list (l, nsid);
+
+      return l;
+    }
+#endif
 
+  if (mode & RTLD_NOLOAD)
+    {
+      /* We are not supposed to load the object unless it is already
+	 loaded.  So return now.  */
+      free (realname);
+      __close_nocancel (fd);
+      return NULL;
+    }
+
+  /* Print debugging message.  */
+  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+    _dl_debug_printf ("file=%s [%lu];  generating link map\n", name, nsid);
+
+  /* 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 (_ld_map_object_1 (l, fd, fbp, mode, loader, stack_endp, &errval,
+                        &errstring))
+    goto lose;
+
+  /* We are done mapping in the file.  We no longer need the descriptor.  */
+  if (__glibc_unlikely (__close_nocancel (fd) != 0))
+    {
+      errstring = N_("cannot close file descriptor");
+      goto lose_errno;
+    }
+  /* Signal that we closed the file.  */
+  fd = -1;
+
+  _ld_map_object_2 (l, mode, id, origname, nsid, r, &make_consistent);
   return l;
 }
-\f
+
 /* Print search path.  */
 static void
 print_search_path (struct r_search_path_elem **list,
-- 
2.37.2


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

* [PATCH 05/13] elf: split open_verify() into reusable parts
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (3 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 04/13] elf: split _dl_map_object_from_fd() into reusable parts Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-18 16:51 ` [PATCH 06/13] elf: load elf hdr fully in open_verify() Stas Sergeev
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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 9e0e63b9a3..353dc2aa13 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1598,19 +1598,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
@@ -1637,7 +1629,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))
@@ -1663,161 +1818,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] 107+ messages in thread

* [PATCH 06/13] elf: load elf hdr fully in open_verify()
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (4 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 05/13] elf: split open_verify() " Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-18 16:51 ` [PATCH 07/13] elf: convert pread64 to callback in do_open_verify() Stas Sergeev
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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 353dc2aa13..45656d8fa5 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)
 {
@@ -955,18 +979,8 @@ _ld_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
@@ -1629,31 +1643,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
@@ -1754,19 +1753,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,
@@ -1992,16 +1989,16 @@ open_path (const char *name, size_t namelen, int mode,
 
 /* Map in the shared object file NAME.  */
 
-struct link_map *
-_dl_map_object (struct link_map *loader, const char *name,
-		int type, int trace_mode, int mode, Lmid_t nsid)
+static struct link_map *
+___dl_map_object (struct link_map *loader, const char *name,
+		  int type, int trace_mode, int mode, Lmid_t nsid,
+		  struct filebuf *fbp)
 {
   int fd;
   const char *origname = NULL;
   char *realname;
   char *name_copy;
   struct link_map *l;
-  struct filebuf fb;
 
   assert (nsid >= 0);
   assert (nsid < GL(dl_nns));
@@ -2091,7 +2088,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;
@@ -2107,7 +2104,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
@@ -2121,7 +2118,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);
 	    }
 	}
@@ -2129,7 +2126,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);
 
@@ -2138,7 +2135,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)
@@ -2147,7 +2144,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)
@@ -2201,7 +2198,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))
@@ -2219,7 +2216,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))
@@ -2235,7 +2232,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))
@@ -2296,10 +2293,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] 107+ messages in thread

* [PATCH 07/13] elf: convert pread64 to callback in do_open_verify()
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (5 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 06/13] elf: load elf hdr fully in open_verify() Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-18 16:51 ` [PATCH 08/13] elf: convert _dl_map_segments's mmap() to a callback Stas Sergeev
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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 45656d8fa5..67e4933735 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1613,10 +1613,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
@@ -1652,7 +1660,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
@@ -1754,7 +1762,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)
     {
@@ -1815,9 +1823,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] 107+ messages in thread

* [PATCH 08/13] elf: convert _dl_map_segments's mmap() to a callback
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (6 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 07/13] elf: convert pread64 to callback in do_open_verify() Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-18 16:51 ` [PATCH 09/13] elf: call _dl_map_segment() via premap callback Stas Sergeev
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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 67e4933735..17f16c7dd0 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -954,11 +954,12 @@ _dl_process_pt_gnu_property (struct link_map *l, int fd, const ElfW(Phdr) *ph)
 }
 
 static int
-_ld_map_object_1 (struct link_map *l, int fd,
+_ld_map_object_1 (struct link_map *l, void *fd,
                   struct filebuf *fbp,
                   int mode, struct link_map *loader,
                   void **stack_endp, int *errval_p,
-                  const char **errstring_p)
+                  const char **errstring_p,
+                  __typeof (do_mmap) *m_map)
 {
   const ElfW(Ehdr) *header;
   const ElfW(Phdr) *phdr;
@@ -1158,7 +1159,7 @@ _ld_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.  */
@@ -1425,6 +1426,14 @@ _ld_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 = *(const int *) arg;
+  return __mmap (addr, length, prot, flags, fd, offset);
+}
+
 #ifndef EXTERNAL_MAP_FROM_FD
 static
 #endif
@@ -1554,8 +1563,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
       goto lose_errno;
     }
 
-  if (_ld_map_object_1 (l, fd, fbp, mode, loader, stack_endp, &errval,
-                        &errstring))
+  if (_ld_map_object_1 (l, &fd, fbp, mode, loader, stack_endp, &errval,
+                        &errstring, do_mmap))
     goto lose;
 
   /* We are done mapping in the file.  We no longer need the descriptor.  */
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 9af8cae188..a5c041c740 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -78,11 +78,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;
 
@@ -142,7 +142,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] 107+ messages in thread

* [PATCH 09/13] elf: call _dl_map_segment() via premap callback
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (7 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 08/13] elf: convert _dl_map_segments's mmap() to a callback Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-18 16:51 ` [PATCH 10/13] elf: convert _dl_map_object to a callback Stas Sergeev
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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 17f16c7dd0..d54a8f59ac 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -959,7 +959,8 @@ _ld_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;
@@ -1159,7 +1160,7 @@ _ld_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.  */
@@ -1434,6 +1435,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
@@ -1564,7 +1571,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
     }
 
   if (_ld_map_object_1 (l, &fd, fbp, mode, loader, stack_endp, &errval,
-                        &errstring, do_mmap))
+                        &errstring, do_mmap, do_map_segment))
     goto lose;
 
   /* We are done mapping in the file.  We no longer need the descriptor.  */
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 a5c041c740..43a59752bb 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -82,7 +82,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;
 
@@ -104,7 +105,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] 107+ messages in thread

* [PATCH 10/13] elf: convert _dl_map_object to a callback
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (8 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 09/13] elf: call _dl_map_segment() via premap callback Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-18 16:51 ` [PATCH 11/13] elf: split _dl_check_loaded() from _dl_map_object Stas Sergeev
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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 d54a8f59ac..46bf44efa3 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2322,8 +2322,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;
@@ -2334,6 +2334,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 91a2d8a538..f3886c21bc 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -40,6 +40,7 @@
 
 #include <dl-dst.h>
 #include <dl-prop.h>
+#include <dl-main.h>
 
 
 /* We must be careful not to leave us in an inconsistent state.  Thus we
@@ -48,6 +49,7 @@
 struct dl_open_args
 {
   const char *file;
+  void *private;
   int mode;
   /* This is the caller of the dlopen() function.  */
   const void *caller_dlopen;
@@ -55,6 +57,10 @@ struct dl_open_args
   /* Namespace ID.  */
   Lmid_t nsid;
 
+  struct link_map *
+  (*dl_map) (struct link_map *loader, const char *name, void *private,
+             int type, int trace_mode, int mode, Lmid_t nsid);
+
   /* Original value of _ns_global_scope_pending_adds.  Set by
      dl_open_worker.  Only valid if nsid is a real namespace
      (non-negative).  */
@@ -531,7 +537,7 @@ dl_open_worker_begin (void *a)
 
   /* Load the named object.  */
   struct link_map *new;
-  args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,
+  args->map = new = args->dl_map (call_map, file, args->private, lt_loaded, 0,
 				    mode | __RTLD_CALLMAP, args->nsid);
 
   /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is
@@ -818,9 +824,11 @@ dl_open_worker (void *a)
 		      new->l_name, new->l_ns, new->l_direct_opencount);
 }
 
-void *
-_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
-	  int argc, char *argv[], char *env[])
+static void *
+do_dl_open (const char *file, void *private, int mode,
+            const void *caller_dlopen, Lmid_t nsid,
+            int argc, char *argv[], char *env[],
+            __typeof (__dl_map_object) *dl_map)
 {
   if ((mode & RTLD_BINDING_MASK) == 0)
     /* One of the flags must be set.  */
@@ -870,10 +878,12 @@ no more namespaces available for dlmopen()"));
 
   struct dl_open_args args;
   args.file = file;
+  args.private = private;
   args.mode = mode;
   args.caller_dlopen = caller_dlopen;
   args.map = NULL;
   args.nsid = nsid;
+  args.dl_map = dl_map;
   /* args.libc_already_loaded is always assigned by dl_open_worker
      (before any explicit/non-local returns).  */
   args.argc = argc;
@@ -935,6 +945,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] 107+ messages in thread

* [PATCH 11/13] elf: split _dl_check_loaded() from _dl_map_object
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (9 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 10/13] elf: convert _dl_map_object to a callback Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-18 16:51 ` [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767] Stas Sergeev
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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 | 37 ++++++++++++++++++++++++-------------
 1 file changed, 24 insertions(+), 13 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 46bf44efa3..7175b99962 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2011,23 +2011,11 @@ open_path (const char *name, size_t namelen, int mode,
   return -1;
 }
 
-/* Map in the shared object file NAME.  */
-
 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)
+_dl_check_loaded(const char *name, Lmid_t nsid)
 {
-  int fd;
-  const char *origname = NULL;
-  char *realname;
-  char *name_copy;
   struct link_map *l;
 
-  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,
@@ -2056,6 +2044,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.  */
+
+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;
+
+  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] 107+ messages in thread

* [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (10 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 11/13] elf: split _dl_check_loaded() from _dl_map_object Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-29 13:45   ` Carlos O'Donell
  2023-03-18 16:51 ` [PATCH 13/13] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag Stas Sergeev
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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                                |   5 +-
 dlfcn/Versions                                |   3 +
 dlfcn/dlfcn.h                                 |  22 +++
 dlfcn/dlmem.c                                 | 102 +++++++++++
 dlfcn/tst-dlmem-fdlopen.c                     | 106 +++++++++++
 elf/dl-load.c                                 | 169 ++++++++++++++++++
 elf/dl-load.h                                 |   3 +
 elf/dl-main.h                                 |  12 ++
 elf/dl-open.c                                 |  13 ++
 elf/rtld.c                                    |   1 +
 include/dlfcn.h                               |   4 +
 manual/dynlink.texi                           |   1 +
 sysdeps/generic/ldsodefs.h                    |   9 +
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 48 files changed, 484 insertions(+), 1 deletion(-)
 create mode 100644 dlfcn/dlmem.c
 create mode 100644 dlfcn/tst-dlmem-fdlopen.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 1fa7fea1ef..8bca644168 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -28,6 +28,7 @@ routines = \
   dlclose \
   dlerror \
   dlinfo \
+  dlmem \
   dlmopen \
   dlopen \
   dlsym \
@@ -51,7 +52,8 @@ endif
 ifeq (yes,$(build-shared))
 tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
 	bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
-	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen
+	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem-fdlopen
+CPPFLAGS-tst-dlmem-fdlopen.c += -DBUILDDIR=\"$(objpfx)\"
 endif
 modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
 		defaultmod2 errmsg1mod modatexit modcxaatexit \
@@ -102,6 +104,7 @@ $(objpfx)glrefmain.out: $(objpfx)glrefmain \
 $(objpfx)failtest.out: $(objpfx)failtestmod.so
 
 $(objpfx)tst-dladdr.out: $(objpfx)glreflib1.so
+$(objpfx)tst-dlmem-fdlopen.out: $(objpfx)glreflib1.so
 
 $(objpfx)tst-dlinfo.out: $(objpfx)glreflib3.so
 LDFLAGS-glreflib3.so = -Wl,-rpath,:
diff --git a/dlfcn/Versions b/dlfcn/Versions
index cc34eb824d..b427c9c3a3 100644
--- a/dlfcn/Versions
+++ b/dlfcn/Versions
@@ -28,6 +28,9 @@ libc {
     dlsym;
     dlvsym;
   }
+  GLIBC_2.38 {
+    dlmem;
+  }
   GLIBC_PRIVATE {
     __libc_dlerror_result;
     _dlerror_run;
diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h
index c5d192597d..87dc4932fd 100644
--- a/dlfcn/dlfcn.h
+++ b/dlfcn/dlfcn.h
@@ -68,6 +68,28 @@ extern void *dlsym (void *__restrict __handle,
 /* Like `dlopen', but request object to be allocated in a new namespace.  */
 extern void *dlmopen (Lmid_t __nsid, const char *__file, int __mode) __THROWNL;
 
+/* Callback for dlmem. */
+typedef void *
+(dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
+	          void *cookie);
+
+struct dlmem_args {
+  /* Optional name to associate with the loaded object. */
+  const char *soname;
+  /* Namespace where to load the object. */
+  Lmid_t nsid;
+  /* dlmem-specific flags. */
+  unsigned flags;
+  /* Optional premap callback. */
+  dlmem_premap_t *premap;
+  /* Optional argument for premap callback. */
+  void *cookie;
+};
+
+/* Like `dlmopen', but loads shared object from memory buffer.  */
+extern void *dlmem (const unsigned char *buffer, size_t size, int mode,
+		    struct dlmem_args *dlm_args);
+
 /* Find the run-time address in the shared object HANDLE refers to
    of the symbol called NAME with VERSION.  */
 extern void *dlvsym (void *__restrict __handle,
diff --git a/dlfcn/dlmem.c b/dlfcn/dlmem.c
new file mode 100644
index 0000000000..548f6f34eb
--- /dev/null
+++ b/dlfcn/dlmem.c
@@ -0,0 +1,102 @@
+/* Load a shared object from memory.
+   Copyright (C) 1995-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <shlib-compat.h>
+
+struct _dlmem_args
+{
+  /* The arguments for dlmem_doit.  */
+  const unsigned char *buffer;
+  size_t size;
+  int mode;
+  struct dlmem_args *args;
+  /* The return value of dlmem_doit.  */
+  void *new;
+  /* Address of the caller.  */
+  const void *caller;
+};
+
+static void
+dlmem_doit (void *a)
+{
+  struct _dlmem_args *args = (struct _dlmem_args *) a;
+
+  if (args->mode & ~(RTLD_BINDING_MASK | RTLD_NOLOAD | RTLD_DEEPBIND
+		     | RTLD_GLOBAL | RTLD_LOCAL | RTLD_NODELETE
+		     | __RTLD_SPROF))
+    _dl_signal_error (0, NULL, NULL, _("invalid mode parameter"));
+
+  args->new = GLRO(dl_mem) (args->buffer, args->size,
+			    args->mode | __RTLD_DLOPEN,
+			    args->args,
+			    args->caller,
+			    __libc_argc, __libc_argv, __environ);
+}
+
+
+static void *
+dlmem_implementation (const unsigned char *buffer, size_t size, int mode,
+		      struct dlmem_args *dlm_args, void *dl_caller)
+{
+  struct _dlmem_args args;
+  args.buffer = buffer;
+  args.size = size;
+  args.mode = mode;
+  args.args = dlm_args;
+  args.caller = dl_caller;
+
+  return _dlerror_run (dlmem_doit, &args) ? NULL : args.new;
+}
+
+#ifdef SHARED
+void *
+___dlmem (const unsigned char *buffer, size_t size, int mode,
+	  struct dlmem_args *dlm_args)
+{
+  if (GLRO (dl_dlfcn_hook) != NULL)
+    return GLRO (dl_dlfcn_hook)->dlmem (buffer, size, mode, dlm_args,
+                                        RETURN_ADDRESS (0));
+  else
+    return dlmem_implementation (buffer, size, mode, dlm_args,
+				 RETURN_ADDRESS (0));
+}
+versioned_symbol (libc, ___dlmem, dlmem, GLIBC_2_38);
+
+#else /* !SHARED */
+/* Also used with _dlfcn_hook.  */
+void *
+__dlmem (const unsigned char *buffer, size_t size, int mode,
+	 struct dlmem_args *dlm_args, void *dl_caller)
+{
+  return dlmem_implementation (buffer, size, mode, dlm_args, dl_caller);
+}
+
+void *
+___dlmem (const unsigned char *buffer, size_t size, int mode,
+	  struct dlmem_args *dlm_args)
+{
+  return __dlmem (buffer, size, mode, dlm_args, RETURN_ADDRESS (0));
+}
+weak_alias (___dlmem, dlmem)
+static_link_warning (dlmem)
+#endif /* !SHARED */
diff --git a/dlfcn/tst-dlmem-fdlopen.c b/dlfcn/tst-dlmem-fdlopen.c
new file mode 100644
index 0000000000..db9b5cf54a
--- /dev/null
+++ b/dlfcn/tst-dlmem-fdlopen.c
@@ -0,0 +1,106 @@
+/* Test for fdlopen implementation on top of dlmem.
+   Copyright (C) 2000-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <link.h>
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <support/check.h>
+
+static void *
+fdlopen (int fd, int flags)
+{
+  off_t len;
+  void *addr;
+  void *handle;
+
+  len = lseek (fd, 0, SEEK_END);
+  lseek (fd, 0, SEEK_SET);
+  addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (addr == MAP_FAILED)
+    {
+      printf ("cannot mmap, %s\n", strerror(errno));
+      exit (EXIT_FAILURE);
+    }
+  handle = dlmem (addr, len, flags, NULL);
+  munmap (addr, len);
+  return handle;
+}
+
+
+#define TEST_FUNCTION do_test
+extern int do_test (void);
+
+int
+do_test (void)
+{
+  char cmd[256];
+  void *handle;
+  int (*sym) (void); /* We load ref1 from glreflib1.c.  */
+  Dl_info info;
+  int rc;
+  int fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
+  if (fd == -1)
+    error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
+  handle = fdlopen (fd, RTLD_NOW);
+  close (fd);
+  if (handle == NULL)
+    {
+      printf ("fdlopen failed, %s\n", dlerror());
+      exit (EXIT_FAILURE);
+    }
+
+  /* Check that the lib is properly mmap()ed, rather than memcpy()ed.
+     This may fail on linux kernels <5.13. */
+  snprintf (cmd, sizeof(cmd), "grep glreflib1.so /proc/%i/maps", getpid());
+  rc = system (cmd);
+  TEST_COMPARE (rc, 0);
+
+  sym = dlsym (handle, "ref1");
+  if (sym == NULL)
+    error (EXIT_FAILURE, 0, "dlsym failed");
+
+  memset (&info, 0, sizeof (info));
+  rc = dladdr (sym, &info);
+  if (rc == 0)
+    error (EXIT_FAILURE, 0, "dladdr failed");
+
+  printf ("info.dli_fname = %p (\"%s\")\n", info.dli_fname, info.dli_fname);
+  printf ("info.dli_fbase = %p\n", info.dli_fbase);
+  printf ("info.dli_sname = %p (\"%s\")\n", info.dli_sname, info.dli_sname);
+  printf ("info.dli_saddr = %p\n", info.dli_saddr);
+
+  if (info.dli_fname == NULL)
+    error (EXIT_FAILURE, 0, "dli_fname is NULL");
+  if (info.dli_fbase == NULL)
+    error (EXIT_FAILURE, 0, "dli_fbase is NULL");
+  if (info.dli_sname == NULL)
+    error (EXIT_FAILURE, 0, "dli_sname is NULL");
+  if (info.dli_saddr == NULL)
+    error (EXIT_FAILURE, 0, "dli_saddr is NULL");
+  return 0;
+}
+
+
+#include <support/test-driver.c>
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 7175b99962..2550173f48 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -75,6 +75,7 @@ struct filebuf
 #include <dl-machine-reject-phdr.h>
 #include <dl-sysdep-open.h>
 #include <dl-prop.h>
+#include <dl-main.h>
 #include <not-cancel.h>
 
 #include <endian.h>
@@ -2353,6 +2354,174 @@ _dl_map_object (struct link_map *loader, const char *name,
   return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid);
 }
 
+static void *
+do_memremap (void *addr, size_t length, int prot, int flags,
+             void *arg, off_t offset)
+{
+  const struct dlmem_fbuf *fb = arg;
+
+  assert (flags & MAP_FIXED);
+  if (offset < fb->len)
+    {
+      size_t to_copy = length;
+      if (offset + to_copy > fb->len)
+        to_copy = fb->len - offset;
+#ifdef MREMAP_DONTUNMAP
+      void *addr2 = __mremap ((void *) (fb->buf + offset), to_copy, to_copy,
+                              MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_DONTUNMAP,
+                              addr);
+      /* MREMAP_DONTUNMAP introduced in linux-5.7, but only works for
+         file-based maps since commit a460938 went in 5.13.
+         So have a fall-back. */
+      if (addr2 == MAP_FAILED)
+        memcpy (addr, fb->buf + offset, to_copy);
+#else
+      /* MREMAP_DONTUNMAP is not always available. This is a fall-back. */
+      memcpy (addr, fb->buf + offset, to_copy);
+#endif
+    }
+  if (__mprotect (addr, length, prot) == -1)
+    return MAP_FAILED;
+  return addr;
+}
+
+static void *
+do_dlmem_premap (void *mappref, size_t maplength, size_t mapalign,
+		 void *cookie)
+{
+  struct dlmem_fbuf *fb = cookie;
+  void *ret = MAP_FAILED;
+
+  if (fb->dlm_args && fb->dlm_args->premap)
+    ret = fb->dlm_args->premap (mappref, maplength, mapalign,
+                                fb->dlm_args->cookie);
+  if (ret == MAP_FAILED)
+    ret = (void *) _dl_map_segment ((ElfW(Addr)) mappref, maplength,
+                                    mapalign);
+  return ret;
+}
+
+static ssize_t
+do_pread_memcpy (void *arg, void *buf, size_t count, off_t offset)
+{
+  struct dlmem_fbuf *fb = arg;
+  if (offset >= fb->len)
+    return -1;
+  if (offset + count > fb->len)
+    count = fb->len - offset;
+  if (count)
+    memcpy (buf, fb->buf + offset, count);
+  return count;
+}
+
+static struct link_map *
+___dl_map_object_from_mem (struct link_map *loader, const char *name,
+			   void *private, int type, int trace_mode,
+			   int mode, Lmid_t nsid, struct filebuf *fbp)
+{
+  struct link_map *l;
+  int err;
+  char *realname;
+  /* Initialize to keep the compiler happy.  */
+  const char *errstring = NULL;
+  int errval = 0;
+  struct r_debug *r = _dl_debug_update (nsid);
+  bool make_consistent = false;
+  struct r_file_id id = {};
+
+  assert (nsid >= 0);
+  assert (nsid < GL(dl_nns));
+
+  if (name && *name)
+    {
+      /* Look for this name among those already loaded.  */
+      l = _dl_check_loaded (name, nsid);
+      if (l)
+        return l;
+    }
+
+  /* Will be true if we found a DSO which is of the other ELF class.  */
+  bool found_other_class = false;
+
+  err = do_open_verify (name, private, fbp,
+                        loader ?: GL(dl_ns)[nsid]._ns_loaded,
+                        &found_other_class, false, do_pread_memcpy);
+  if (err)
+    return NULL;
+
+  /* In case the LOADER information has only been provided to get to
+     the appropriate RUNPATH/RPATH information we do not need it
+     anymore.  */
+  if (mode & __RTLD_CALLMAP)
+    loader = NULL;
+
+  if (mode & RTLD_NOLOAD)
+    {
+      /* We are not supposed to load the object unless it is already
+	 loaded.  So return now.  */
+      return NULL;
+    }
+
+  /* Print debugging message.  */
+  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+    _dl_debug_printf ("dlmem [%lu];  generating link map\n", nsid);
+
+  /* _dl_new_object() treats "" separately and doesn't free it. */
+  realname = *name ? __strdup (name) : (char *) "";
+  /* Enter the new object in the list of loaded objects.  */
+  l = _dl_new_object (realname, name, type, loader, mode, nsid);
+  if (__glibc_unlikely (l == NULL))
+    {
+      errstring = N_("cannot create shared object descriptor");
+      goto lose_errno;
+    }
+
+  void *stack_end = __libc_stack_end;
+  if (_ld_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval,
+                        &errstring, do_memremap, do_dlmem_premap))
+    goto lose;
+
+  _ld_map_object_2 (l, mode, id, NULL, nsid, r, &make_consistent);
+  return l;
+
+lose_errno:
+  errval = errno;
+lose:
+  if (l != NULL && l->l_map_start != 0)
+    _dl_unmap_segments (l);
+  if (l != NULL && l->l_origin != (char *) -1l)
+    free ((char *) l->l_origin);
+  if (l != NULL && !l->l_libname->dont_free)
+    free (l->l_libname);
+  if (l != NULL && l->l_phdr_allocated)
+    free ((void *) l->l_phdr);
+  free (l);
+
+  if (make_consistent && r != NULL)
+    {
+      r->r_state = RT_CONSISTENT;
+      _dl_debug_state ();
+      LIBC_PROBE (map_failed, 2, nsid, r);
+    }
+
+  _dl_signal_error (errval, NULL, NULL, errstring);
+  return NULL;
+}
+
+struct link_map *
+__dl_map_object_from_mem (struct link_map *loader, const char *name,
+			  void *private, int type, int trace_mode,
+			  int mode, Lmid_t nsid)
+{
+  struct link_map *ret;
+  struct filebuf fb = {};
+
+  ret = ___dl_map_object_from_mem (loader, name, private, type, trace_mode,
+                                  mode, nsid, &fb);
+  filebuf_done (&fb);
+  return ret;
+}
+
 struct add_path_state
 {
   bool counting;
diff --git a/elf/dl-load.h b/elf/dl-load.h
index e777da5838..09b2878260 100644
--- a/elf/dl-load.h
+++ b/elf/dl-load.h
@@ -106,6 +106,9 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
                                       - c->mapoff);
 }
 
+static void *
+do_mmap (void *addr, size_t length, int prot, int flags,
+         void *arg, off_t offset);
 
 /* This is a subroutine of _dl_map_object_from_fd.  It is responsible
    for filling in several fields in *L: l_map_start, l_map_end, l_addr,
diff --git a/elf/dl-main.h b/elf/dl-main.h
index 344a87d5e8..e60fafaeb6 100644
--- a/elf/dl-main.h
+++ b/elf/dl-main.h
@@ -104,6 +104,13 @@ struct dl_main_state
   bool version_info;
 };
 
+struct dlmem_fbuf
+{
+  ssize_t len;
+  const unsigned char *buf;
+  struct dlmem_args *dlm_args;
+};
+
 /* Open the shared object NAME and map in its segments.
    LOADER's DT_RPATH is used in searching for NAME.
    If the object is already opened, returns its existing map.  */
@@ -112,6 +119,11 @@ __dl_map_object (struct link_map *loader,
                  const char *name, void *private,
                  int type, int trace_mode, int mode,
                  Lmid_t nsid) attribute_hidden;
+extern struct link_map *
+__dl_map_object_from_mem (struct link_map *loader,
+			  const char *name, void *private,
+			  int type, int trace_mode, int mode,
+			  Lmid_t nsid) attribute_hidden;
 
 /* Helper function to invoke _dl_init_paths with the right arguments
    from *STATE.  */
diff --git a/elf/dl-open.c b/elf/dl-open.c
index f3886c21bc..7c7bb2ef4f 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -953,6 +953,19 @@ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
                      __dl_map_object);
 }
 
+void *
+_dl_mem (const unsigned char *buffer, size_t size, int mode,
+	 struct dlmem_args *dlm_args, const void *caller_dlopen,
+	 int argc, char *argv[], char *env[])
+{
+  struct dlmem_fbuf fb = { .buf = buffer, .len = size, .dlm_args = dlm_args };
+  Lmid_t nsid = dlm_args ? dlm_args->nsid : LM_ID_BASE;
+  const char *file = (dlm_args && dlm_args->soname) ? dlm_args->soname : "";
+
+  return do_dl_open (file, &fb, mode, caller_dlopen, nsid, argc, argv, env,
+                     __dl_map_object_from_mem);
+}
+
 void
 _dl_show_scope (struct link_map *l, int from)
 {
diff --git a/elf/rtld.c b/elf/rtld.c
index f82fbeb132..1877ec9f16 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -370,6 +370,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
     ._dl_mcount = _dl_mcount,
     ._dl_lookup_symbol_x = _dl_lookup_symbol_x,
     ._dl_open = _dl_open,
+    ._dl_mem = _dl_mem,
     ._dl_close = _dl_close,
     ._dl_catch_error = _dl_catch_error,
     ._dl_error_free = _dl_error_free,
diff --git a/include/dlfcn.h b/include/dlfcn.h
index ae25f05303..6205cf407d 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -100,6 +100,8 @@ struct dlfcn_hook
 {
   /* Public interfaces.  */
   void *(*dlopen) (const char *file, int mode, void *dl_caller);
+  void *(*dlmem) (const unsigned char *buffer, size_t size, int mode,
+		  struct dlmem_args *dlm_args, void *dl_caller);
   int (*dlclose) (void *handle);
   void *(*dlsym) (void *handle, const char *name, void *dl_caller);
   void *(*dlvsym) (void *handle, const char *name, const char *version,
@@ -123,6 +125,8 @@ struct dlfcn_hook
    the __libc_dl* functions defined in elf/dl-libc.c instead.  */
 
 extern void *__dlopen (const char *file, int mode, void *caller);
+extern void *__dlmem (const unsigned char *file, size_t size, int mode,
+		      struct dlmem_args *dlm_args, void *caller);
 extern void *__dlmopen (Lmid_t nsid, const char *file, int mode,
 			void *dl_caller);
 extern int __dlclose (void *handle);
diff --git a/manual/dynlink.texi b/manual/dynlink.texi
index 6a4a50d3f0..21bc6c067c 100644
--- a/manual/dynlink.texi
+++ b/manual/dynlink.texi
@@ -209,6 +209,7 @@ This function is a GNU extension.
 @c dladdr1
 @c dlclose
 @c dlerror
+@c dlmem
 @c dlmopen
 @c dlopen
 @c dlsym
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index c99dad77cc..abca95b18c 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -669,6 +669,9 @@ struct rtld_global_ro
 				   struct link_map *);
   void *(*_dl_open) (const char *file, int mode, const void *caller_dlopen,
 		     Lmid_t nsid, int argc, char *argv[], char *env[]);
+  void *(*_dl_mem) (const unsigned char *buffer, size_t size, int mode,
+		    struct dlmem_args *dlm_args, const void *caller_dlopen,
+		    int argc, char *argv[], char *env[]);
   void (*_dl_close) (void *map);
   /* libdl in a secondary namespace (after dlopen) must use
      _dl_catch_error from the main namespace, so it has to be
@@ -1249,6 +1252,12 @@ extern void *_dl_open (const char *name, int mode, const void *caller,
 		       Lmid_t nsid, int argc, char *argv[], char *env[])
      attribute_hidden;
 
+/* Open shared object from memory buffer. */
+extern void *_dl_mem (const unsigned char *buffer, size_t size, int mode,
+		      struct dlmem_args *dlm_args, const void *caller,
+		      int argc, char *argv[], char *env[])
+     attribute_hidden;
+
 /* Free or queue for freeing scope OLD.  If other threads might be
    in the middle of _dl_fixup, _dl_profile_fixup or dl*sym using the
    old scope, OLD can't be freed until no thread is using it.  */
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index ed0c4789eb..1880004336 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2326,6 +2326,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 0e2d9c3045..c4d31cabed 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2665,3 +2665,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index f1bec1978d..93b37a1e1d 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2774,6 +2774,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
index aa874b88d0..9fec2308c3 100644
--- a/sysdeps/unix/sysv/linux/arc/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
@@ -2426,3 +2426,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index afbd57da6f..bd0f04e58a 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -546,6 +546,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index e7364cd3fe..dccc49c8d6 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -543,6 +543,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 913fa59215..0df8524ac0 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2702,3 +2702,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index 43af3a9811..fa4680e97a 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2651,6 +2651,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index af72f8fab0..bfb1bde49d 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2835,6 +2835,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 48cbb0fa50..e53505abe0 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2600,6 +2600,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
index c15884bb0b..9f56cbdcab 100644
--- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
@@ -2186,3 +2186,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 3738db81df..d5443b1198 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -547,6 +547,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _Exit F
 GLIBC_2.4 _IO_2_1_stderr_ D 0x98
 GLIBC_2.4 _IO_2_1_stdin_ D 0x98
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index ed13627752..640c8b8c4a 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2778,6 +2778,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 8357738621..79b400efc6 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2751,3 +2751,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 58c5da583d..38b4098950 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2748,3 +2748,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index d3741945cd..9a4f909a25 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2743,6 +2743,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 5319fdc204..c8b9f85fdb 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2741,6 +2741,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 1743ea6eb9..0887b67394 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2749,6 +2749,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 9b1f53c6ac..1c3a6f4bee 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2651,6 +2651,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index ae1c6ca1b5..31b23859a4 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2790,3 +2790,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
index a7c572c947..59f4aa7766 100644
--- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
+++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
@@ -2172,3 +2172,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index 074fa031a7..d715d0ae97 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2817,6 +2817,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index dfcb4bd2d5..3addcf3d17 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2850,6 +2850,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 63bbccf3f9..5365978277 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2571,6 +2571,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index ab85fd61ef..f0af576192 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2885,3 +2885,4 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
index b716f5c763..941fd40ea8 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
@@ -2428,3 +2428,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 774e777b65..74f58439ad 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2628,3 +2628,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 8625135c48..cf3a10aa76 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2815,6 +2815,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index d00c7eb262..2ad97f87b2 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2608,6 +2608,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index b63037241d..7c81b94953 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2658,6 +2658,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index d80055617d..0493d6b456 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2655,6 +2655,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 5be55c11d2..6fd09f6499 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2810,6 +2810,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
 GLIBC_2.38 __nldbl___isoc23_vswscanf F
 GLIBC_2.38 __nldbl___isoc23_vwscanf F
 GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 _IO_fprintf F
 GLIBC_2.4 _IO_printf F
 GLIBC_2.4 _IO_sprintf F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 475fdaae15..24dbc4801f 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2623,6 +2623,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 6cfb928bc8..522ca8e8aa 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2574,6 +2574,7 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
 GLIBC_2.4 __confstr_chk F
 GLIBC_2.4 __fgets_chk F
 GLIBC_2.4 __fgets_unlocked_chk F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index c735097172..42b170c805 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2680,3 +2680,4 @@ GLIBC_2.38 __isoc23_wcstoull F
 GLIBC_2.38 __isoc23_wcstoull_l F
 GLIBC_2.38 __isoc23_wcstoumax F
 GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 dlmem F
-- 
2.37.2


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

* [PATCH 13/13] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (11 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767] Stas Sergeev
@ 2023-03-18 16:51 ` Stas Sergeev
  2023-03-29 12:32 ` [PATCH v9 0/13] implement dlmem() function Adhemerval Zanella Netto
  2023-03-29 13:17 ` Carlos O'Donell
  14 siblings, 0 replies; 107+ messages in thread
From: Stas Sergeev @ 2023-03-18 16:51 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         |   3 +
 dlfcn/glreflib1.c     |   2 +
 dlfcn/tst-dlmem-shm.c | 169 ++++++++++++++++++++++++++++++++++++++++++
 elf/dl-load.c         |  25 ++++++-
 5 files changed, 202 insertions(+), 2 deletions(-)
 create mode 100644 dlfcn/tst-dlmem-shm.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 8bca644168..755020af1e 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-fdlopen
+	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem-fdlopen \
+	tst-dlmem-shm
 CPPFLAGS-tst-dlmem-fdlopen.c += -DBUILDDIR=\"$(objpfx)\"
+CPPFLAGS-tst-dlmem-shm.c += -DBUILDDIR=\"$(objpfx)\"
 endif
 modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
 		defaultmod2 errmsg1mod modatexit modcxaatexit \
@@ -105,6 +107,7 @@ $(objpfx)failtest.out: $(objpfx)failtestmod.so
 
 $(objpfx)tst-dladdr.out: $(objpfx)glreflib1.so
 $(objpfx)tst-dlmem-fdlopen.out: $(objpfx)glreflib1.so
+$(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 87dc4932fd..30bb532709 100644
--- a/dlfcn/dlfcn.h
+++ b/dlfcn/dlfcn.h
@@ -73,6 +73,9 @@ typedef void *
 (dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
 	          void *cookie);
 
+/* Do not replace destination mapping. 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 2550173f48..e7216c74c3 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2385,6 +2385,25 @@ do_memremap (void *addr, size_t length, int prot, int flags,
   return addr;
 }
 
+static void *
+do_mmapcpy (void *addr, size_t length, int prot, int flags,
+            void *arg, off_t offset)
+{
+  const struct dlmem_fbuf *fb = arg;
+
+  assert (flags & MAP_FIXED);
+  if (offset < fb->len)
+    {
+      size_t to_copy = length;
+      if (offset + to_copy > fb->len)
+        to_copy = fb->len - offset;
+      memcpy (addr, fb->buf + offset, 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)
@@ -2428,6 +2447,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));
@@ -2478,7 +2501,7 @@ ___dl_map_object_from_mem (struct link_map *loader, const char *name,
 
   void *stack_end = __libc_stack_end;
   if (_ld_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval,
-                        &errstring, do_memremap, do_dlmem_premap))
+                        &errstring, m_map, do_dlmem_premap))
     goto lose;
 
   _ld_map_object_2 (l, mode, id, NULL, nsid, r, &make_consistent);
-- 
2.37.2


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (12 preceding siblings ...)
  2023-03-18 16:51 ` [PATCH 13/13] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag Stas Sergeev
@ 2023-03-29 12:32 ` Adhemerval Zanella Netto
  2023-03-29 13:10   ` stsp
  2023-03-29 13:18   ` stsp
  2023-03-29 13:17 ` Carlos O'Donell
  14 siblings, 2 replies; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 12:32 UTC (permalink / raw)
  To: Stas Sergeev, libc-alpha, janderson, Carlos O'Donell, Rich Felker



On 18/03/23 13:50, Stas Sergeev via Libc-alpha wrote:
> 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.

Hi Stas,

This discussion has been dragging without much conclusion while you have sent
multiple iterations of the same proposal without addressing the initial issues
Carlos, Jonathan, and myself have raised.

I will not focus on your specific usercase since it is quite singular and I 
think will not be applicable to a libc interface (mapping a VM memory with
different bit size and gluing all together should be an application domain
instead of bleeding out the interface to the libc).

Jonathan already summarized the main issue with dlmem:

  - dlmem() seems to to expect the user to parse the program headers and mmap()
  the binary as required. That requires the application to re-implement a core,
  delicate piece of ld.so... and do so correctly. From an API design perspective,
  that seems like a very poor choice of abstraction.

As well Carlos:

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

And this alone seems to me that this interface is not easily composable and 
adds a lot of corner cases if the user does not provide an expected ELF image.
This is in par of what Carlos has raised [1] and IMHO you have not addresses the
issues, since you have focused on how this interface applies to your specific
problem instead of see who it fits as a generic libc interface (I will let
Carlos comment further).

As a generic interface I also don't see how this would really fit, it means
that process need to map a memory segment that was generated either through
a JIT or through an external process that can not provide a named file access
(due security limitation).  For these a fdlopen similar to BSD makes more
sense: for former memfd_create can be used, while for later either shm_open 
or passing the fd through unix sockets.

So I don't think dlmem really fits on glibc, although I might take a loot
on the internal code refactoring. I am ccing Rich Felker, creator of musl
libc, since he might have some additional insights whether this interface
make sense or not (and he usually have good ones).

[1] https://sourceware.org/pipermail/libc-alpha/2023-February/145735.html

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-29 12:32 ` [PATCH v9 0/13] implement dlmem() function Adhemerval Zanella Netto
@ 2023-03-29 13:10   ` stsp
  2023-03-29 13:18   ` stsp
  1 sibling, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-29 13:10 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Hi Adhemerval, thanks for a follow-up!

29.03.2023 17:32, Adhemerval Zanella Netto пишет:
> This discussion has been dragging without much conclusion while you 
> have sent
> multiple iterations of the same proposal without addressing the initial issues
> Carlos, Jonathan, and myself have raised.
>
> I will not focus on your specific usercase since it is quite singular and I
> think will not be applicable to a libc interface (mapping a VM memory with
> different bit size and gluing all together should be an application domain
> instead of bleeding out the interface to the libc).

Yes, which is why I am trying to put in
the "widely" requested in the past dlmem()
function, which is quite agnostic to my use-case
but allows me to accomplish the task.


> Jonathan already summarized the main issue with dlmem:
>
>    - dlmem() seems to to expect the user to parse the program headers and mmap()
>    the binary as required. That requires the application to re-implement a core,
>    delicate piece of ld.so... and do so correctly. From an API design perspective,
>    that seems like a very poor choice of abstraction.
>
> As well Carlos:
>
>    * No guarantee that the memory that is loaded meets the preconditions for the
>    dynamic loader and application uses.
>
> And this alone seems to me that this interface is not easily composable and

OK, yes, I've heard all of you, but what
should I do? I can SWEAR that my test-cases
do not parse the elf headers. I can swear
that I have no idea what are you talking about.
Well, I did that already, but nothing helps. :(
So how should I proceed with the claim that
was said by everyone, and yet never confirmed
by a single fact?
I do not parse any elf headers, maybe glibc does,
I do not! If this is wrong, please point me
IN MY TEST-CASES that I actually do this.

> adds a lot of corner cases if the user does not provide an expected ELF image.
> This is in par of what Carlos has raised [1] and IMHO you have not addresses the
> issues, since you have focused on how this interface applies to your specific
> problem instead of see who it fits as a generic libc interface (I will let
> Carlos comment further).

I have not addressed that issue, I have
not seen that issue, I don't believe this
issue even exist.
What should I do?
Please, be constructive.
You can point it to me IF it is there.

> So I don't think dlmem really fits on glibc, although I might take a loot
> on the internal code refactoring. I am ccing Rich Felker, creator of musl
> libc, since he might have some additional insights whether this interface
> make sense or not (and he usually have good ones).
Thanks!
I appreciate the involvement of Rich Felker,
or any other activity to bring that discussion
further. But we need to get off the aforementioned
question around the elf headers.

Also please note that I have a v10 locally with
some fixes and new test-cases, so v9 should
not be reviewed quite literally.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
                   ` (13 preceding siblings ...)
  2023-03-29 12:32 ` [PATCH v9 0/13] implement dlmem() function Adhemerval Zanella Netto
@ 2023-03-29 13:17 ` Carlos O'Donell
  2023-03-29 13:26   ` stsp
  2023-03-29 17:03   ` stsp
  14 siblings, 2 replies; 107+ messages in thread
From: Carlos O'Donell @ 2023-03-29 13:17 UTC (permalink / raw)
  To: Stas Sergeev, libc-alpha

On 3/18/23 12:50, Stas Sergeev via Libc-alpha wrote:

A cover letter needs to explain in detail what the series does and why glibc
should include the series e.g. use cases, workloads.

Adding a new API is a serious thing for glibc since we will keep the new interface
for a long time, like the Linux kernel, and need to ensure that it is a generic
enough building block that we can use.

Please provide the details here about why glibc should incldue dlmem() and which
use cases it enables that existing APIs can't easily support.

> 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
> 

-- 
Cheers,
Carlos.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-29 12:32 ` [PATCH v9 0/13] implement dlmem() function Adhemerval Zanella Netto
  2023-03-29 13:10   ` stsp
@ 2023-03-29 13:18   ` stsp
  2023-03-31 12:20     ` Szabolcs Nagy
  1 sibling, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-29 13:18 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Just to be more constructive, here is the
example:

void *dlopen_with_offset(const char *file, off_t offset, int flags)
{
     off_t len;
     void *addr;
     void *handle;
     int fd;

     fd = open(file, O_RDONLY);
     if (fd == -1)
         return NULL;
     len = lseek(fd, 0, SEEK_END);
     lseek(fd, 0, SEEK_SET);
     if (len <= offset)
       goto err_close;
     len -= offset;
     /* if offset unaligned then just use read() */
     if (offset & (PAGE_SIZE - 1)) {
         int rd;
         addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
                     MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
         if (addr == MAP_FAILED)
             goto err_close;
         rd = read(fd, addr, len);
         if (rd != len) {
             munmap(addr, len);
             goto err_close;
         }
     } else {
         addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, offset);
     }
     close(fd);
     if (addr == MAP_FAILED)
         return NULL;
     handle = dlmem(addr, len, flags, NULL);
     munmap(addr, len);
     return handle;

err_close:
     close(fd);
     return NULL;
}


Does it parse an elf headers?
If so - where?


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-29 13:17 ` Carlos O'Donell
@ 2023-03-29 13:26   ` stsp
  2023-03-29 17:03   ` stsp
  1 sibling, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-29 13:26 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha

Hi,

29.03.2023 18:17, Carlos O'Donell пишет:
> On 3/18/23 12:50, Stas Sergeev via Libc-alpha wrote:
>
> A cover letter needs to explain in detail what the series does and why glibc
> should include the series e.g. use cases, workloads.
>
> Adding a new API is a serious thing for glibc since we will keep the new interface
> for a long time, like the Linux kernel, and need to ensure that it is a generic
> enough building block that we can use.
>
> Please provide the details here about why glibc should incldue dlmem() and which
> use cases it enables that existing APIs can't easily support.
OK, I'll post the extensive description
along with v10 soon, thanks!

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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-18 16:51 ` [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767] Stas Sergeev
@ 2023-03-29 13:45   ` Carlos O'Donell
  2023-03-29 13:51     ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Carlos O'Donell @ 2023-03-29 13:45 UTC (permalink / raw)
  To: Stas Sergeev, libc-alpha

On 3/18/23 12:51, Stas Sergeev via Libc-alpha wrote:
> This patch adds the following function:
> void *dlmem(const unsigned char *buffer, size_t size, int flags,
>             struct dlmem_args *dlm_args);

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

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

In my experience it is the wrong level of abstraction.

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

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

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

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

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

There we tended to do:

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

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

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

(a) Useful for many applications.

(b) Does not require complex logic on the application part.
    - or -
    Supports the application by providing another API to do loading.
 
> It is the same as dlopen() but allows to dynamic-link solibs from
> the memory buffer, rather than from a file as dlopen() does.
> 
> "buffer" arg is the pointer to the solib image in memory.
> "size" is the solib image size. Must be smaller-or-equal to the
>     actual buffer size.
> "flags" is the same flags argument used in dlopen().
> "dlm_args" is an optional argument that allows to specify the load
>     namespace and a premap callback.
> 
> This implementation is trying a "zero-copy" technique first, but
> it works only with linux kernels 5.13 and newer. So the memcpy()
> fall-back is added as well.
> 
> This patch adds a test-case named tst-dlmem-fdlopen. It implements
> a bsd-compatible fdlopen() on top of dlmem() and loads a test lib
> with it. It then checks /proc/<pid>/maps to make sure the library
> was mmap()ed rather then memcopied. Then it does the regular set
> of solib tests.
> 
> The test-suite was run on x86_64/64 and showed no regressions.
> 
> Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
> ---
>  dlfcn/Makefile                                |   5 +-
>  dlfcn/Versions                                |   3 +
>  dlfcn/dlfcn.h                                 |  22 +++
>  dlfcn/dlmem.c                                 | 102 +++++++++++
>  dlfcn/tst-dlmem-fdlopen.c                     | 106 +++++++++++
>  elf/dl-load.c                                 | 169 ++++++++++++++++++
>  elf/dl-load.h                                 |   3 +
>  elf/dl-main.h                                 |  12 ++
>  elf/dl-open.c                                 |  13 ++
>  elf/rtld.c                                    |   1 +
>  include/dlfcn.h                               |   4 +
>  manual/dynlink.texi                           |   1 +
>  sysdeps/generic/ldsodefs.h                    |   9 +
>  sysdeps/mach/hurd/i386/libc.abilist           |   1 +
>  sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
>  sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
>  sysdeps/unix/sysv/linux/arc/libc.abilist      |   1 +
>  sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
>  sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
>  sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
>  sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
>  sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
>  sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
>  .../sysv/linux/loongarch/lp64/libc.abilist    |   1 +
>  .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
>  .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
>  .../sysv/linux/microblaze/be/libc.abilist     |   1 +
>  .../sysv/linux/microblaze/le/libc.abilist     |   1 +
>  .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
>  .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
>  .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
>  .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
>  sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
>  sysdeps/unix/sysv/linux/or1k/libc.abilist     |   1 +
>  .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
>  .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
>  .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
>  .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
>  .../unix/sysv/linux/riscv/rv32/libc.abilist   |   1 +
>  .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
>  .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
>  .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
>  sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
>  sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
>  .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
>  .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
>  .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
>  .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
>  48 files changed, 484 insertions(+), 1 deletion(-)
>  create mode 100644 dlfcn/dlmem.c
>  create mode 100644 dlfcn/tst-dlmem-fdlopen.c
> 
> diff --git a/dlfcn/Makefile b/dlfcn/Makefile
> index 1fa7fea1ef..8bca644168 100644
> --- a/dlfcn/Makefile
> +++ b/dlfcn/Makefile
> @@ -28,6 +28,7 @@ routines = \
>    dlclose \
>    dlerror \
>    dlinfo \
> +  dlmem \
>    dlmopen \
>    dlopen \
>    dlsym \
> @@ -51,7 +52,8 @@ endif
>  ifeq (yes,$(build-shared))
>  tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
>  	bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
> -	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen
> +	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem-fdlopen
> +CPPFLAGS-tst-dlmem-fdlopen.c += -DBUILDDIR=\"$(objpfx)\"
>  endif
>  modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
>  		defaultmod2 errmsg1mod modatexit modcxaatexit \
> @@ -102,6 +104,7 @@ $(objpfx)glrefmain.out: $(objpfx)glrefmain \
>  $(objpfx)failtest.out: $(objpfx)failtestmod.so
>  
>  $(objpfx)tst-dladdr.out: $(objpfx)glreflib1.so
> +$(objpfx)tst-dlmem-fdlopen.out: $(objpfx)glreflib1.so
>  
>  $(objpfx)tst-dlinfo.out: $(objpfx)glreflib3.so
>  LDFLAGS-glreflib3.so = -Wl,-rpath,:
> diff --git a/dlfcn/Versions b/dlfcn/Versions
> index cc34eb824d..b427c9c3a3 100644
> --- a/dlfcn/Versions
> +++ b/dlfcn/Versions
> @@ -28,6 +28,9 @@ libc {
>      dlsym;
>      dlvsym;
>    }
> +  GLIBC_2.38 {
> +    dlmem;
> +  }
>    GLIBC_PRIVATE {
>      __libc_dlerror_result;
>      _dlerror_run;
> diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h
> index c5d192597d..87dc4932fd 100644
> --- a/dlfcn/dlfcn.h
> +++ b/dlfcn/dlfcn.h
> @@ -68,6 +68,28 @@ extern void *dlsym (void *__restrict __handle,
>  /* Like `dlopen', but request object to be allocated in a new namespace.  */
>  extern void *dlmopen (Lmid_t __nsid, const char *__file, int __mode) __THROWNL;
>  
> +/* Callback for dlmem. */
> +typedef void *
> +(dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
> +	          void *cookie);
> +
> +struct dlmem_args {
> +  /* Optional name to associate with the loaded object. */
> +  const char *soname;
> +  /* Namespace where to load the object. */
> +  Lmid_t nsid;
> +  /* dlmem-specific flags. */
> +  unsigned flags;
> +  /* Optional premap callback. */
> +  dlmem_premap_t *premap;
> +  /* Optional argument for premap callback. */
> +  void *cookie;
> +};
> +
> +/* Like `dlmopen', but loads shared object from memory buffer.  */
> +extern void *dlmem (const unsigned char *buffer, size_t size, int mode,
> +		    struct dlmem_args *dlm_args);
> +
>  /* Find the run-time address in the shared object HANDLE refers to
>     of the symbol called NAME with VERSION.  */
>  extern void *dlvsym (void *__restrict __handle,
> diff --git a/dlfcn/dlmem.c b/dlfcn/dlmem.c
> new file mode 100644
> index 0000000000..548f6f34eb
> --- /dev/null
> +++ b/dlfcn/dlmem.c
> @@ -0,0 +1,102 @@
> +/* Load a shared object from memory.
> +   Copyright (C) 1995-2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <dlfcn.h>
> +#include <libintl.h>
> +#include <stddef.h>
> +#include <unistd.h>
> +#include <ldsodefs.h>
> +#include <shlib-compat.h>
> +
> +struct _dlmem_args
> +{
> +  /* The arguments for dlmem_doit.  */
> +  const unsigned char *buffer;
> +  size_t size;
> +  int mode;
> +  struct dlmem_args *args;
> +  /* The return value of dlmem_doit.  */
> +  void *new;
> +  /* Address of the caller.  */
> +  const void *caller;
> +};
> +
> +static void
> +dlmem_doit (void *a)
> +{
> +  struct _dlmem_args *args = (struct _dlmem_args *) a;
> +
> +  if (args->mode & ~(RTLD_BINDING_MASK | RTLD_NOLOAD | RTLD_DEEPBIND
> +		     | RTLD_GLOBAL | RTLD_LOCAL | RTLD_NODELETE
> +		     | __RTLD_SPROF))
> +    _dl_signal_error (0, NULL, NULL, _("invalid mode parameter"));
> +
> +  args->new = GLRO(dl_mem) (args->buffer, args->size,
> +			    args->mode | __RTLD_DLOPEN,
> +			    args->args,
> +			    args->caller,
> +			    __libc_argc, __libc_argv, __environ);
> +}
> +
> +
> +static void *
> +dlmem_implementation (const unsigned char *buffer, size_t size, int mode,
> +		      struct dlmem_args *dlm_args, void *dl_caller)
> +{
> +  struct _dlmem_args args;
> +  args.buffer = buffer;
> +  args.size = size;
> +  args.mode = mode;
> +  args.args = dlm_args;
> +  args.caller = dl_caller;
> +
> +  return _dlerror_run (dlmem_doit, &args) ? NULL : args.new;
> +}
> +
> +#ifdef SHARED
> +void *
> +___dlmem (const unsigned char *buffer, size_t size, int mode,
> +	  struct dlmem_args *dlm_args)
> +{
> +  if (GLRO (dl_dlfcn_hook) != NULL)
> +    return GLRO (dl_dlfcn_hook)->dlmem (buffer, size, mode, dlm_args,
> +                                        RETURN_ADDRESS (0));
> +  else
> +    return dlmem_implementation (buffer, size, mode, dlm_args,
> +				 RETURN_ADDRESS (0));
> +}
> +versioned_symbol (libc, ___dlmem, dlmem, GLIBC_2_38);
> +
> +#else /* !SHARED */
> +/* Also used with _dlfcn_hook.  */
> +void *
> +__dlmem (const unsigned char *buffer, size_t size, int mode,
> +	 struct dlmem_args *dlm_args, void *dl_caller)
> +{
> +  return dlmem_implementation (buffer, size, mode, dlm_args, dl_caller);
> +}
> +
> +void *
> +___dlmem (const unsigned char *buffer, size_t size, int mode,
> +	  struct dlmem_args *dlm_args)
> +{
> +  return __dlmem (buffer, size, mode, dlm_args, RETURN_ADDRESS (0));
> +}
> +weak_alias (___dlmem, dlmem)
> +static_link_warning (dlmem)
> +#endif /* !SHARED */
> diff --git a/dlfcn/tst-dlmem-fdlopen.c b/dlfcn/tst-dlmem-fdlopen.c
> new file mode 100644
> index 0000000000..db9b5cf54a
> --- /dev/null
> +++ b/dlfcn/tst-dlmem-fdlopen.c
> @@ -0,0 +1,106 @@
> +/* Test for fdlopen implementation on top of dlmem.
> +   Copyright (C) 2000-2022 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <dlfcn.h>
> +#include <link.h>
> +#include <errno.h>
> +#include <error.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <sys/mman.h>
> +#include <support/check.h>
> +
> +static void *
> +fdlopen (int fd, int flags)
> +{
> +  off_t len;
> +  void *addr;
> +  void *handle;
> +
> +  len = lseek (fd, 0, SEEK_END);
> +  lseek (fd, 0, SEEK_SET);
> +  addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);

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

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


> +  if (addr == MAP_FAILED)
> +    {
> +      printf ("cannot mmap, %s\n", strerror(errno));
> +      exit (EXIT_FAILURE);
> +    }
> +  handle = dlmem (addr, len, flags, NULL);
> +  munmap (addr, len);
> +  return handle;
> +}
> +
> +
> +#define TEST_FUNCTION do_test
> +extern int do_test (void);
> +
> +int
> +do_test (void)
> +{
> +  char cmd[256];
> +  void *handle;
> +  int (*sym) (void); /* We load ref1 from glreflib1.c.  */
> +  Dl_info info;
> +  int rc;
> +  int fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
> +  if (fd == -1)
> +    error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
> +  handle = fdlopen (fd, RTLD_NOW);
> +  close (fd);
> +  if (handle == NULL)
> +    {
> +      printf ("fdlopen failed, %s\n", dlerror());
> +      exit (EXIT_FAILURE);
> +    }
> +
> +  /* Check that the lib is properly mmap()ed, rather than memcpy()ed.
> +     This may fail on linux kernels <5.13. */
> +  snprintf (cmd, sizeof(cmd), "grep glreflib1.so /proc/%i/maps", getpid());
> +  rc = system (cmd);
> +  TEST_COMPARE (rc, 0);
> +
> +  sym = dlsym (handle, "ref1");
> +  if (sym == NULL)
> +    error (EXIT_FAILURE, 0, "dlsym failed");
> +
> +  memset (&info, 0, sizeof (info));
> +  rc = dladdr (sym, &info);
> +  if (rc == 0)
> +    error (EXIT_FAILURE, 0, "dladdr failed");
> +
> +  printf ("info.dli_fname = %p (\"%s\")\n", info.dli_fname, info.dli_fname);
> +  printf ("info.dli_fbase = %p\n", info.dli_fbase);
> +  printf ("info.dli_sname = %p (\"%s\")\n", info.dli_sname, info.dli_sname);
> +  printf ("info.dli_saddr = %p\n", info.dli_saddr);
> +
> +  if (info.dli_fname == NULL)
> +    error (EXIT_FAILURE, 0, "dli_fname is NULL");
> +  if (info.dli_fbase == NULL)
> +    error (EXIT_FAILURE, 0, "dli_fbase is NULL");
> +  if (info.dli_sname == NULL)
> +    error (EXIT_FAILURE, 0, "dli_sname is NULL");
> +  if (info.dli_saddr == NULL)
> +    error (EXIT_FAILURE, 0, "dli_saddr is NULL");
> +  return 0;
> +}
> +
> +
> +#include <support/test-driver.c>
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 7175b99962..2550173f48 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -75,6 +75,7 @@ struct filebuf
>  #include <dl-machine-reject-phdr.h>
>  #include <dl-sysdep-open.h>
>  #include <dl-prop.h>
> +#include <dl-main.h>
>  #include <not-cancel.h>
>  
>  #include <endian.h>
> @@ -2353,6 +2354,174 @@ _dl_map_object (struct link_map *loader, const char *name,
>    return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid);
>  }
>  
> +static void *
> +do_memremap (void *addr, size_t length, int prot, int flags,
> +             void *arg, off_t offset)
> +{
> +  const struct dlmem_fbuf *fb = arg;
> +
> +  assert (flags & MAP_FIXED);
> +  if (offset < fb->len)
> +    {
> +      size_t to_copy = length;
> +      if (offset + to_copy > fb->len)
> +        to_copy = fb->len - offset;
> +#ifdef MREMAP_DONTUNMAP
> +      void *addr2 = __mremap ((void *) (fb->buf + offset), to_copy, to_copy,
> +                              MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_DONTUNMAP,
> +                              addr);
> +      /* MREMAP_DONTUNMAP introduced in linux-5.7, but only works for
> +         file-based maps since commit a460938 went in 5.13.
> +         So have a fall-back. */
> +      if (addr2 == MAP_FAILED)
> +        memcpy (addr, fb->buf + offset, to_copy);
> +#else
> +      /* MREMAP_DONTUNMAP is not always available. This is a fall-back. */
> +      memcpy (addr, fb->buf + offset, to_copy);
> +#endif
> +    }
> +  if (__mprotect (addr, length, prot) == -1)
> +    return MAP_FAILED;
> +  return addr;
> +}
> +
> +static void *
> +do_dlmem_premap (void *mappref, size_t maplength, size_t mapalign,
> +		 void *cookie)
> +{
> +  struct dlmem_fbuf *fb = cookie;
> +  void *ret = MAP_FAILED;
> +
> +  if (fb->dlm_args && fb->dlm_args->premap)
> +    ret = fb->dlm_args->premap (mappref, maplength, mapalign,
> +                                fb->dlm_args->cookie);
> +  if (ret == MAP_FAILED)
> +    ret = (void *) _dl_map_segment ((ElfW(Addr)) mappref, maplength,
> +                                    mapalign);
> +  return ret;
> +}
> +
> +static ssize_t
> +do_pread_memcpy (void *arg, void *buf, size_t count, off_t offset)
> +{
> +  struct dlmem_fbuf *fb = arg;
> +  if (offset >= fb->len)
> +    return -1;
> +  if (offset + count > fb->len)
> +    count = fb->len - offset;
> +  if (count)
> +    memcpy (buf, fb->buf + offset, count);
> +  return count;
> +}
> +
> +static struct link_map *
> +___dl_map_object_from_mem (struct link_map *loader, const char *name,
> +			   void *private, int type, int trace_mode,
> +			   int mode, Lmid_t nsid, struct filebuf *fbp)
> +{
> +  struct link_map *l;
> +  int err;
> +  char *realname;
> +  /* Initialize to keep the compiler happy.  */
> +  const char *errstring = NULL;
> +  int errval = 0;
> +  struct r_debug *r = _dl_debug_update (nsid);
> +  bool make_consistent = false;
> +  struct r_file_id id = {};
> +
> +  assert (nsid >= 0);
> +  assert (nsid < GL(dl_nns));
> +
> +  if (name && *name)
> +    {
> +      /* Look for this name among those already loaded.  */
> +      l = _dl_check_loaded (name, nsid);
> +      if (l)
> +        return l;
> +    }
> +
> +  /* Will be true if we found a DSO which is of the other ELF class.  */
> +  bool found_other_class = false;
> +
> +  err = do_open_verify (name, private, fbp,
> +                        loader ?: GL(dl_ns)[nsid]._ns_loaded,
> +                        &found_other_class, false, do_pread_memcpy);
> +  if (err)
> +    return NULL;
> +
> +  /* In case the LOADER information has only been provided to get to
> +     the appropriate RUNPATH/RPATH information we do not need it
> +     anymore.  */
> +  if (mode & __RTLD_CALLMAP)
> +    loader = NULL;
> +
> +  if (mode & RTLD_NOLOAD)
> +    {
> +      /* We are not supposed to load the object unless it is already
> +	 loaded.  So return now.  */
> +      return NULL;
> +    }
> +
> +  /* Print debugging message.  */
> +  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
> +    _dl_debug_printf ("dlmem [%lu];  generating link map\n", nsid);
> +
> +  /* _dl_new_object() treats "" separately and doesn't free it. */
> +  realname = *name ? __strdup (name) : (char *) "";
> +  /* Enter the new object in the list of loaded objects.  */
> +  l = _dl_new_object (realname, name, type, loader, mode, nsid);
> +  if (__glibc_unlikely (l == NULL))
> +    {
> +      errstring = N_("cannot create shared object descriptor");
> +      goto lose_errno;
> +    }
> +
> +  void *stack_end = __libc_stack_end;
> +  if (_ld_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval,
> +                        &errstring, do_memremap, do_dlmem_premap))
> +    goto lose;
> +
> +  _ld_map_object_2 (l, mode, id, NULL, nsid, r, &make_consistent);
> +  return l;
> +
> +lose_errno:
> +  errval = errno;
> +lose:
> +  if (l != NULL && l->l_map_start != 0)
> +    _dl_unmap_segments (l);
> +  if (l != NULL && l->l_origin != (char *) -1l)
> +    free ((char *) l->l_origin);
> +  if (l != NULL && !l->l_libname->dont_free)
> +    free (l->l_libname);
> +  if (l != NULL && l->l_phdr_allocated)
> +    free ((void *) l->l_phdr);
> +  free (l);
> +
> +  if (make_consistent && r != NULL)
> +    {
> +      r->r_state = RT_CONSISTENT;
> +      _dl_debug_state ();
> +      LIBC_PROBE (map_failed, 2, nsid, r);
> +    }
> +
> +  _dl_signal_error (errval, NULL, NULL, errstring);
> +  return NULL;
> +}
> +
> +struct link_map *
> +__dl_map_object_from_mem (struct link_map *loader, const char *name,
> +			  void *private, int type, int trace_mode,
> +			  int mode, Lmid_t nsid)
> +{
> +  struct link_map *ret;
> +  struct filebuf fb = {};
> +
> +  ret = ___dl_map_object_from_mem (loader, name, private, type, trace_mode,
> +                                  mode, nsid, &fb);
> +  filebuf_done (&fb);
> +  return ret;
> +}
> +
>  struct add_path_state
>  {
>    bool counting;
> diff --git a/elf/dl-load.h b/elf/dl-load.h
> index e777da5838..09b2878260 100644
> --- a/elf/dl-load.h
> +++ b/elf/dl-load.h
> @@ -106,6 +106,9 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
>                                        - c->mapoff);
>  }
>  
> +static void *
> +do_mmap (void *addr, size_t length, int prot, int flags,
> +         void *arg, off_t offset);
>  
>  /* This is a subroutine of _dl_map_object_from_fd.  It is responsible
>     for filling in several fields in *L: l_map_start, l_map_end, l_addr,
> diff --git a/elf/dl-main.h b/elf/dl-main.h
> index 344a87d5e8..e60fafaeb6 100644
> --- a/elf/dl-main.h
> +++ b/elf/dl-main.h
> @@ -104,6 +104,13 @@ struct dl_main_state
>    bool version_info;
>  };
>  
> +struct dlmem_fbuf
> +{
> +  ssize_t len;
> +  const unsigned char *buf;
> +  struct dlmem_args *dlm_args;
> +};
> +
>  /* Open the shared object NAME and map in its segments.
>     LOADER's DT_RPATH is used in searching for NAME.
>     If the object is already opened, returns its existing map.  */
> @@ -112,6 +119,11 @@ __dl_map_object (struct link_map *loader,
>                   const char *name, void *private,
>                   int type, int trace_mode, int mode,
>                   Lmid_t nsid) attribute_hidden;
> +extern struct link_map *
> +__dl_map_object_from_mem (struct link_map *loader,
> +			  const char *name, void *private,
> +			  int type, int trace_mode, int mode,
> +			  Lmid_t nsid) attribute_hidden;
>  
>  /* Helper function to invoke _dl_init_paths with the right arguments
>     from *STATE.  */
> diff --git a/elf/dl-open.c b/elf/dl-open.c
> index f3886c21bc..7c7bb2ef4f 100644
> --- a/elf/dl-open.c
> +++ b/elf/dl-open.c
> @@ -953,6 +953,19 @@ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
>                       __dl_map_object);
>  }
>  
> +void *
> +_dl_mem (const unsigned char *buffer, size_t size, int mode,
> +	 struct dlmem_args *dlm_args, const void *caller_dlopen,
> +	 int argc, char *argv[], char *env[])
> +{
> +  struct dlmem_fbuf fb = { .buf = buffer, .len = size, .dlm_args = dlm_args };
> +  Lmid_t nsid = dlm_args ? dlm_args->nsid : LM_ID_BASE;
> +  const char *file = (dlm_args && dlm_args->soname) ? dlm_args->soname : "";
> +
> +  return do_dl_open (file, &fb, mode, caller_dlopen, nsid, argc, argv, env,
> +                     __dl_map_object_from_mem);
> +}
> +
>  void
>  _dl_show_scope (struct link_map *l, int from)
>  {
> diff --git a/elf/rtld.c b/elf/rtld.c
> index f82fbeb132..1877ec9f16 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -370,6 +370,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
>      ._dl_mcount = _dl_mcount,
>      ._dl_lookup_symbol_x = _dl_lookup_symbol_x,
>      ._dl_open = _dl_open,
> +    ._dl_mem = _dl_mem,
>      ._dl_close = _dl_close,
>      ._dl_catch_error = _dl_catch_error,
>      ._dl_error_free = _dl_error_free,
> diff --git a/include/dlfcn.h b/include/dlfcn.h
> index ae25f05303..6205cf407d 100644
> --- a/include/dlfcn.h
> +++ b/include/dlfcn.h
> @@ -100,6 +100,8 @@ struct dlfcn_hook
>  {
>    /* Public interfaces.  */
>    void *(*dlopen) (const char *file, int mode, void *dl_caller);
> +  void *(*dlmem) (const unsigned char *buffer, size_t size, int mode,
> +		  struct dlmem_args *dlm_args, void *dl_caller);
>    int (*dlclose) (void *handle);
>    void *(*dlsym) (void *handle, const char *name, void *dl_caller);
>    void *(*dlvsym) (void *handle, const char *name, const char *version,
> @@ -123,6 +125,8 @@ struct dlfcn_hook
>     the __libc_dl* functions defined in elf/dl-libc.c instead.  */
>  
>  extern void *__dlopen (const char *file, int mode, void *caller);
> +extern void *__dlmem (const unsigned char *file, size_t size, int mode,
> +		      struct dlmem_args *dlm_args, void *caller);
>  extern void *__dlmopen (Lmid_t nsid, const char *file, int mode,
>  			void *dl_caller);
>  extern int __dlclose (void *handle);
> diff --git a/manual/dynlink.texi b/manual/dynlink.texi
> index 6a4a50d3f0..21bc6c067c 100644
> --- a/manual/dynlink.texi
> +++ b/manual/dynlink.texi
> @@ -209,6 +209,7 @@ This function is a GNU extension.
>  @c dladdr1
>  @c dlclose
>  @c dlerror
> +@c dlmem
>  @c dlmopen
>  @c dlopen
>  @c dlsym
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index c99dad77cc..abca95b18c 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -669,6 +669,9 @@ struct rtld_global_ro
>  				   struct link_map *);
>    void *(*_dl_open) (const char *file, int mode, const void *caller_dlopen,
>  		     Lmid_t nsid, int argc, char *argv[], char *env[]);
> +  void *(*_dl_mem) (const unsigned char *buffer, size_t size, int mode,
> +		    struct dlmem_args *dlm_args, const void *caller_dlopen,
> +		    int argc, char *argv[], char *env[]);
>    void (*_dl_close) (void *map);
>    /* libdl in a secondary namespace (after dlopen) must use
>       _dl_catch_error from the main namespace, so it has to be
> @@ -1249,6 +1252,12 @@ extern void *_dl_open (const char *name, int mode, const void *caller,
>  		       Lmid_t nsid, int argc, char *argv[], char *env[])
>       attribute_hidden;
>  
> +/* Open shared object from memory buffer. */
> +extern void *_dl_mem (const unsigned char *buffer, size_t size, int mode,
> +		      struct dlmem_args *dlm_args, const void *caller,
> +		      int argc, char *argv[], char *env[])
> +     attribute_hidden;
> +
>  /* Free or queue for freeing scope OLD.  If other threads might be
>     in the middle of _dl_fixup, _dl_profile_fixup or dl*sym using the
>     old scope, OLD can't be freed until no thread is using it.  */
> diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
> index ed0c4789eb..1880004336 100644
> --- a/sysdeps/mach/hurd/i386/libc.abilist
> +++ b/sysdeps/mach/hurd/i386/libc.abilist
> @@ -2326,6 +2326,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index 0e2d9c3045..c4d31cabed 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2665,3 +2665,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index f1bec1978d..93b37a1e1d 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -2774,6 +2774,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _IO_fprintf F
>  GLIBC_2.4 _IO_printf F
>  GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist
> index aa874b88d0..9fec2308c3 100644
> --- a/sysdeps/unix/sysv/linux/arc/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist
> @@ -2426,3 +2426,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> index afbd57da6f..bd0f04e58a 100644
> --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
> @@ -546,6 +546,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _Exit F
>  GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
>  GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
> diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> index e7364cd3fe..dccc49c8d6 100644
> --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
> @@ -543,6 +543,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _Exit F
>  GLIBC_2.4 _IO_2_1_stderr_ D 0xa0
>  GLIBC_2.4 _IO_2_1_stdin_ D 0xa0
> diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
> index 913fa59215..0df8524ac0 100644
> --- a/sysdeps/unix/sysv/linux/csky/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
> @@ -2702,3 +2702,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> index 43af3a9811..fa4680e97a 100644
> --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
> @@ -2651,6 +2651,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index af72f8fab0..bfb1bde49d 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2835,6 +2835,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> index 48cbb0fa50..e53505abe0 100644
> --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
> @@ -2600,6 +2600,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> index c15884bb0b..9f56cbdcab 100644
> --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist
> @@ -2186,3 +2186,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index 3738db81df..d5443b1198 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -547,6 +547,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _Exit F
>  GLIBC_2.4 _IO_2_1_stderr_ D 0x98
>  GLIBC_2.4 _IO_2_1_stdin_ D 0x98
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index ed13627752..640c8b8c4a 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -2778,6 +2778,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> index 8357738621..79b400efc6 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
> @@ -2751,3 +2751,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> index 58c5da583d..38b4098950 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
> @@ -2748,3 +2748,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index d3741945cd..9a4f909a25 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -2743,6 +2743,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index 5319fdc204..c8b9f85fdb 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -2741,6 +2741,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 1743ea6eb9..0887b67394 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -2749,6 +2749,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index 9b1f53c6ac..1c3a6f4bee 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -2651,6 +2651,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> index ae1c6ca1b5..31b23859a4 100644
> --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
> @@ -2790,3 +2790,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist
> index a7c572c947..59f4aa7766 100644
> --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist
> @@ -2172,3 +2172,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index 074fa031a7..d715d0ae97 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -2817,6 +2817,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _IO_fprintf F
>  GLIBC_2.4 _IO_printf F
>  GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index dfcb4bd2d5..3addcf3d17 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -2850,6 +2850,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _IO_fprintf F
>  GLIBC_2.4 _IO_printf F
>  GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> index 63bbccf3f9..5365978277 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
> @@ -2571,6 +2571,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _IO_fprintf F
>  GLIBC_2.4 _IO_printf F
>  GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> index ab85fd61ef..f0af576192 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
> @@ -2885,3 +2885,4 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> index b716f5c763..941fd40ea8 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist
> @@ -2428,3 +2428,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> index 774e777b65..74f58439ad 100644
> --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
> @@ -2628,3 +2628,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 8625135c48..cf3a10aa76 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -2815,6 +2815,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _IO_fprintf F
>  GLIBC_2.4 _IO_printf F
>  GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index d00c7eb262..2ad97f87b2 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -2608,6 +2608,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _IO_fprintf F
>  GLIBC_2.4 _IO_printf F
>  GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> index b63037241d..7c81b94953 100644
> --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
> @@ -2658,6 +2658,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> index d80055617d..0493d6b456 100644
> --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
> @@ -2655,6 +2655,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 5be55c11d2..6fd09f6499 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -2810,6 +2810,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
>  GLIBC_2.38 __nldbl___isoc23_vswscanf F
>  GLIBC_2.38 __nldbl___isoc23_vwscanf F
>  GLIBC_2.38 __nldbl___isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 _IO_fprintf F
>  GLIBC_2.4 _IO_printf F
>  GLIBC_2.4 _IO_sprintf F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index 475fdaae15..24dbc4801f 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -2623,6 +2623,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index 6cfb928bc8..522ca8e8aa 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -2574,6 +2574,7 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F
>  GLIBC_2.4 __confstr_chk F
>  GLIBC_2.4 __fgets_chk F
>  GLIBC_2.4 __fgets_unlocked_chk F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index c735097172..42b170c805 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2680,3 +2680,4 @@ GLIBC_2.38 __isoc23_wcstoull F
>  GLIBC_2.38 __isoc23_wcstoull_l F
>  GLIBC_2.38 __isoc23_wcstoumax F
>  GLIBC_2.38 __isoc23_wscanf F
> +GLIBC_2.38 dlmem F

-- 
Cheers,
Carlos.


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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 13:45   ` Carlos O'Donell
@ 2023-03-29 13:51     ` stsp
  2023-03-29 14:10       ` Jonathon Anderson
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-29 13:51 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha


29.03.2023 18:45, Carlos O'Donell пишет:
> On 3/18/23 12:51, Stas Sergeev via Libc-alpha wrote:
>> This patch adds the following function:
>> void *dlmem(const unsigned char *buffer, size_t size, int flags,
>>              struct dlmem_args *dlm_args);
> I am raising a sustained objection to including dlmem() in glibc.
>
> I appreciate your effort in working on this serious, and I think *many* of
> the core changes you propose are good cleanups.
>
> In my experience it is the wrong level of abstraction.
>
> To implement fdlopen on top of dlmem requires PT_LOAD processing and that
> will duplicate into userspace a significant part of the complexity of ELF
> loading and segment handling. The test case you include below is incomplete
> when it comes to implementing a functional fdlopen() across the supported
> architectures and toolchain variants.

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

Where, just where have you seen it in my
patch? Or if its not there, why do you mention
it?


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

* Re: [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100]
  2023-03-18 16:50 ` [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
@ 2023-03-29 13:54   ` Adhemerval Zanella Netto
  2023-03-29 14:12     ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 13:54 UTC (permalink / raw)
  To: Stas Sergeev, libc-alpha



On 18/03/23 13:50, Stas Sergeev via Libc-alpha wrote:
> _dl_close_worker() has this code:
>       /* This name always is allocated.  */
>       free (imap->l_name);
> 
> But in that particular case, while indeed being allocated, l_name
> doesn't point to the start of an allocation:
>   new = (struct link_map *) calloc (sizeof (*new) + audit_space
>                                     + sizeof (struct link_map *)
>                                     + sizeof (*newname) + libname_len, 1);
>   ...
>   new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1)
>                                                             + audit_space);
> 
>   new->l_libname = newname
>     = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1);
>   newname->name = (char *) memcpy (newname + 1, libname, libname_len);
>   ...
>   new->l_name = (char *) newname->name + libname_len - 1;
> 
> It therefore cannot be freed separately.
> Use strdup("") as a simple fix.

This is not required, the l_name alias to newname->name is only used for 
__RTLD_OPENEXEC (used by loader on DT_NEEDED) and these handlers are not
meant to be dlclose.  So even if the programs get an already opened
handler to a DT_NEEDED object:

  void *h = dlopen ("libc.so.6", RTLD_NOW);
  assert (h != NULL);
  dlclose (h);

The _dl_close_worker comment is indeed correct: the l_name for
dlopen is *always* allocated by open_path so it can always call free on it.

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

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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 13:51     ` stsp
@ 2023-03-29 14:10       ` Jonathon Anderson
  2023-03-29 14:20         ` stsp
  2023-03-30  8:09         ` stsp
  0 siblings, 2 replies; 107+ messages in thread
From: Jonathon Anderson @ 2023-03-29 14:10 UTC (permalink / raw)
  To: stsp, Carlos O'Donell, libc-alpha

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

Stas,

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

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

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

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

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

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

-Jonathon

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

* Re: [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100]
  2023-03-29 13:54   ` Adhemerval Zanella Netto
@ 2023-03-29 14:12     ` stsp
  2023-03-29 14:19       ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-29 14:12 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha


29.03.2023 18:54, Adhemerval Zanella Netto пишет:
>
> On 18/03/23 13:50, Stas Sergeev via Libc-alpha wrote:
>> _dl_close_worker() has this code:
>>        /* This name always is allocated.  */
>>        free (imap->l_name);
>>
>> But in that particular case, while indeed being allocated, l_name
>> doesn't point to the start of an allocation:
>>    new = (struct link_map *) calloc (sizeof (*new) + audit_space
>>                                      + sizeof (struct link_map *)
>>                                      + sizeof (*newname) + libname_len, 1);
>>    ...
>>    new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1)
>>                                                              + audit_space);
>>
>>    new->l_libname = newname
>>      = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1);
>>    newname->name = (char *) memcpy (newname + 1, libname, libname_len);
>>    ...
>>    new->l_name = (char *) newname->name + libname_len - 1;
>>
>> It therefore cannot be freed separately.
>> Use strdup("") as a simple fix.
> This is not required, the l_name alias to newname->name is only used for
> __RTLD_OPENEXEC (used by loader on DT_NEEDED) and these handlers are not
> meant to be dlclose.
But dlmem() can also use "" as the name
if the name is not specified explicitly.
Without that patch it crashes.
I think you mean its not needed w/o dlmem()?
Then its a dlmem-specific patch.

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

* Re: [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100]
  2023-03-29 14:12     ` stsp
@ 2023-03-29 14:19       ` Adhemerval Zanella Netto
  2023-03-29 14:28         ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 14:19 UTC (permalink / raw)
  To: stsp, libc-alpha



On 29/03/23 11:12, stsp wrote:
> 
> 29.03.2023 18:54, Adhemerval Zanella Netto пишет:
>>
>> On 18/03/23 13:50, Stas Sergeev via Libc-alpha wrote:
>>> _dl_close_worker() has this code:
>>>        /* This name always is allocated.  */
>>>        free (imap->l_name);
>>>
>>> But in that particular case, while indeed being allocated, l_name
>>> doesn't point to the start of an allocation:
>>>    new = (struct link_map *) calloc (sizeof (*new) + audit_space
>>>                                      + sizeof (struct link_map *)
>>>                                      + sizeof (*newname) + libname_len, 1);
>>>    ...
>>>    new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1)
>>>                                                              + audit_space);
>>>
>>>    new->l_libname = newname
>>>      = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1);
>>>    newname->name = (char *) memcpy (newname + 1, libname, libname_len);
>>>    ...
>>>    new->l_name = (char *) newname->name + libname_len - 1;
>>>
>>> It therefore cannot be freed separately.
>>> Use strdup("") as a simple fix.
>> This is not required, the l_name alias to newname->name is only used for
>> __RTLD_OPENEXEC (used by loader on DT_NEEDED) and these handlers are not
>> meant to be dlclose.
> But dlmem() can also use "" as the name
> if the name is not specified explicitly.
> Without that patch it crashes.
> I think you mean its not needed w/o dlmem()?

Yes, I did not take in consideration dlmem inclusion for this.  If dlmem breaks
this assumption, it is another issue with the interface.

> Then its a dlmem-specific patch.

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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 14:10       ` Jonathon Anderson
@ 2023-03-29 14:20         ` stsp
  2023-03-29 14:31           ` Adhemerval Zanella Netto
  2023-03-29 14:35           ` Carlos O'Donell
  2023-03-30  8:09         ` stsp
  1 sibling, 2 replies; 107+ messages in thread
From: stsp @ 2023-03-29 14:20 UTC (permalink / raw)
  To: Jonathon Anderson, Carlos O'Donell, libc-alpha


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

Sure as hell p_offset != p_vaddr.
I never ever assumed it does!
OK, if it goes that badly, then I offer you
a deal.
If you present the solib with p_offset!=p_vaddr
and demonstrate that its broken with dlmem(),
and not because some random bug of mine but
exactly because p_offset!=p_vaddr, then I go
away from that dlmem() proposal forever.
If you can't, then you go away.
Do you accept that challenge?
Sorry for offering the silly stuff, but I simply
don't see how to proceed if we are wasting
the time on a things like that.


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

* Re: [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100]
  2023-03-29 14:19       ` Adhemerval Zanella Netto
@ 2023-03-29 14:28         ` stsp
  2023-03-29 14:30           ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-29 14:28 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha


29.03.2023 19:19, Adhemerval Zanella Netto пишет:
>
> On 29/03/23 11:12, stsp wrote:
>> 29.03.2023 18:54, Adhemerval Zanella Netto пишет:
>> But dlmem() can also use "" as the name
>> if the name is not specified explicitly.
>> Without that patch it crashes.
>> I think you mean its not needed w/o dlmem()?
> Yes, I did not take in consideration dlmem inclusion for this.  If dlmem breaks
> this assumption, it is another issue with the interface.
Could you please suggest what should
be done? dlmem() may create the anonymous
objects, name is optional. I noticed that some
code uses "" and did the same.
What is the suggested solution about
creating an anonymous object?
Of course you can prohibit an empty name
for dlmem(), but that will only force people
to fake it with some "123".

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

* Re: [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100]
  2023-03-29 14:28         ` stsp
@ 2023-03-29 14:30           ` Adhemerval Zanella Netto
  2023-03-29 14:33             ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 14:30 UTC (permalink / raw)
  To: stsp, libc-alpha



On 29/03/23 11:28, stsp wrote:
> 
> 29.03.2023 19:19, Adhemerval Zanella Netto пишет:
>>
>> On 29/03/23 11:12, stsp wrote:
>>> 29.03.2023 18:54, Adhemerval Zanella Netto пишет:
>>> But dlmem() can also use "" as the name
>>> if the name is not specified explicitly.
>>> Without that patch it crashes.
>>> I think you mean its not needed w/o dlmem()?
>> Yes, I did not take in consideration dlmem inclusion for this.  If dlmem breaks
>> this assumption, it is another issue with the interface.
> Could you please suggest what should
> be done? dlmem() may create the anonymous
> objects, name is optional. I noticed that some
> code uses "" and did the same.
> What is the suggested solution about
> creating an anonymous object?
> Of course you can prohibit an empty name
> for dlmem(), but that will only force people
> to fake it with some "123".

At least move this change to patch that actually requires it, as an
isolated change it is not required.

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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 14:20         ` stsp
@ 2023-03-29 14:31           ` Adhemerval Zanella Netto
  2023-03-29 15:01             ` stsp
  2023-03-29 14:35           ` Carlos O'Donell
  1 sibling, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 14:31 UTC (permalink / raw)
  To: stsp, Jonathon Anderson, Carlos O'Donell, libc-alpha



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

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

Also, I would like to ask to tune down your tone. This kind of aggressive
way to present technical topics, in a manner of adding 'challengers' to
put pressure on person that raised concerns is not best way and usually
make other developers to avoid engage further in the topic.

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

* Re: [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100]
  2023-03-29 14:30           ` Adhemerval Zanella Netto
@ 2023-03-29 14:33             ` stsp
  0 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-29 14:33 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha


29.03.2023 19:30, Adhemerval Zanella Netto пишет:
> At least move this change to patch that actually requires it, as an
> isolated change it is not required.
Will do, thanks.

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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 14:20         ` stsp
  2023-03-29 14:31           ` Adhemerval Zanella Netto
@ 2023-03-29 14:35           ` Carlos O'Donell
  2023-03-29 14:50             ` stsp
  1 sibling, 1 reply; 107+ messages in thread
From: Carlos O'Donell @ 2023-03-29 14:35 UTC (permalink / raw)
  To: stsp, Jonathon Anderson, libc-alpha

On 3/29/23 10:20, stsp wrote:
> 
> 29.03.2023 19:10, Jonathon Anderson пишет:
>> Stas,
>>
>> Please do some research into the ELF file format. Neither your fdlopen implementation in the test cases nor your dlopen_with_offset implementation in the email chain implement it correctly.
>>
>> AFAICT, the first glaring issue with both of your implementations is that you have neglected the case where p_offset != p_vaddr, i.e. a segment is mmapped to a different location than its layout in the file. There are a LOT of binaries out in the wild where this is the case. Here's a quick one-liner to help you find some on your own box, I have 11712 such binaries on my Debian system:
> 
> Sure as hell p_offset != p_vaddr.
> I never ever assumed it does!
> OK, if it goes that badly, then I offer you
> a deal.

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

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

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

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

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

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

Please be kind to Jonathan.

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

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

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

This is why I don't accept dlmem() as the right level of abstraciton.
It may solve you problem, but in glibc we need to do more than solve
singular problems, we need to provide interfaces that are useful for
many developers and the interfaces should be designed such that they
cannot be easily misused.

-- 
Cheers,
Carlos.


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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 14:35           ` Carlos O'Donell
@ 2023-03-29 14:50             ` stsp
  2023-03-29 15:20               ` Carlos O'Donell
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-29 14:50 UTC (permalink / raw)
  To: Carlos O'Donell, Jonathon Anderson, libc-alpha


29.03.2023 19:35, Carlos O'Donell пишет:
> The fact that such binaries can be created is enough for me to raise
> a sustained objection to the inclusion of dlmem().

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

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

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

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

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


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

No, its not the case.
Its not the case and its not the case.
And I don't know what to do, if I can't
even ask someone to demonstrate why
its the case. :)


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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 14:31           ` Adhemerval Zanella Netto
@ 2023-03-29 15:01             ` stsp
  0 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-29 15:01 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Jonathon Anderson, Carlos O'Donell,
	libc-alpha


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

Do you, Adhemerval, know why my impl is
suspected in not arranging an elf segments
properly?
I'll have to ask everyone personally it seems,
and maybe someone provides a clue finally.

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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 14:50             ` stsp
@ 2023-03-29 15:20               ` Carlos O'Donell
  2023-03-29 15:34                 ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Carlos O'Donell @ 2023-03-29 15:20 UTC (permalink / raw)
  To: stsp, Jonathon Anderson, libc-alpha

On 3/29/23 10:50, stsp wrote:
> 
> 29.03.2023 19:35, Carlos O'Donell пишет:
>> The fact that such binaries can be created is enough for me to raise
>> a sustained objection to the inclusion of dlmem().
> 
> Then someone needs to tell how exactly
> it can be created, and, more importantly,
> point in the implementation the place
> that will then break.

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

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

Are you looking for coaching or mentoring in the design of system interfaces
for dynamic loading?

-- 
Cheers,
Carlos.


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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 15:20               ` Carlos O'Donell
@ 2023-03-29 15:34                 ` stsp
  0 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-29 15:34 UTC (permalink / raw)
  To: Carlos O'Donell, Jonathon Anderson, libc-alpha


29.03.2023 20:20, Carlos O'Donell пишет:
> It is not a reviewers task to show this to you, it is only their task that
> they tell you that it exists. The onus is strongly on the patch submitter to
> produce evidence to the contrary. The reviewers on the list here are a scarce
> resource, and they offer the experience and technical knowledge to help you.

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

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

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

Is this a proof?
If not - what kind of proof do you need?


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

* Re: [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping
  2023-03-18 16:50 ` [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping Stas Sergeev
@ 2023-03-29 17:01   ` Adhemerval Zanella Netto
  2023-03-29 18:00     ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 17:01 UTC (permalink / raw)
  To: libc-alpha, Stas Sergeev



On 18/03/23 13:50, Stas Sergeev via Libc-alpha wrote:
> _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 | 73 +++++++++++++++++++++++--------------------
>  1 file changed, 39 insertions(+), 34 deletions(-)
> 
> diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
> index 504cfc0a41..9af8cae188 100644
> --- a/elf/dl-map-segments.h
> +++ b/elf/dl-map-segments.h
> @@ -22,18 +22,26 @@
>  /* 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;
> +  unsigned map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
> +  unsigned prot = PROT_READ | PROT_WRITE;

glibc code guidelines [1] suggest to explicit define the types, so
'unsigned int' here.

[1] https://sourceware.org/glibc/wiki/Style_and_Conventions

> +
> +#ifdef MAP_DENYWRITE
> +  /* Tell mmap() that we are mapping solib. This flag enables things
> +     like LD_PREFER_MAP_32BIT_EXEC. */
> +  map_flags |= MAP_DENYWRITE;
> +#endif

Why do you need o add MAP_DENYWRITE? They are complete ignored by Linux,
as stated in:

include/linux/mman.h
 31 /*
 32  * The historical set of flags that all mmap implementations implicitly
 33  * support when a ->mmap_validate() op is not provided in file_operations.
 34  *
 35  * MAP_EXECUTABLE and MAP_DENYWRITE are completely ignored throughout the
 36  * kernel.
 37  */
 38 #define LEGACY_MAP_MASK (MAP_SHARED \

(if you grep in Linux source code you will see there are only defined for
historical/compatibility reasons, there is indeed no code that actually uses
it).

> +  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, alocate 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 +49,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;
>  }
>  

So basically it would add another mmap on program loading.  For instance, loading
a simple empty main programs:

* Before:

  openat(AT_FDCWD, "./main", O_RDONLY|O_CLOEXEC) = 3
  mmap(NULL, 16408, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f9d922a9000
  mmap(0x7f9d922aa000, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7f9d922aa000
  mmap(0x7f9d922ab000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f9d922ab000
  mmap(0x7f9d922ac000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f9d922ac000
  [...]
  openat(AT_FDCWD, "/home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
  mmap(NULL, 2080624, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f9d920ab000
  mprotect(0x7f9d920d1000, 1847296, PROT_NONE) = 0
  mmap(0x7f9d920d1000, 1490944, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7f9d920d1000
  mmap(0x7f9d9223d000, 352256, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x192000) = 0x7f9d9223d000
  mmap(0x7f9d92294000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e8000) = 0x7f9d92294000
  mmap(0x7f9d9229a000, 53104, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f9d9229a000

* With this patch:

  openat(AT_FDCWD, "./main", O_RDONLY|O_CLOEXEC) = 3
  mmap(NULL, 16408, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0x7f705da76000
  mmap(0x7f705da76000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x7f705da76000
  mmap(0x7f705da77000, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7f705da77000
  mmap(0x7f705da78000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f705da78000
  mmap(0x7f705da79000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f705da79000
  [...]
  openat(AT_FDCWD, "/home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
  mmap(NULL, 2080624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0x7f705d878000
  mprotect(0x7f705d89e000, 1847296, PROT_NONE) = 0
  mmap(0x7f705d878000, 155648, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x7f705d878000
  mmap(0x7f705d89e000, 1490944, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7f705d89e000
  mmap(0x7f705da0a000, 352256, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x192000) = 0x7f705da0a000
  mmap(0x7f705da61000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e8000) = 0x7f705da61000
  mmap(0x7f705da67000, 53104, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f705da67000

And it also slight change the mapping, using the same program:

* Before:

      0x7ffff7dc2000     0x7ffff7de8000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7de8000     0x7ffff7f54000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7f54000     0x7ffff7faa000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7faa000     0x7ffff7fab000     0x1000   0x1e8000  ---p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7fab000     0x7ffff7faf000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7faf000     0x7ffff7fb1000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so

* With this patch:

      0x7ffff7dc1000     0x7ffff7de7000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7de7000     0x7ffff7f53000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7f53000     0x7ffff7fa9000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7fa9000     0x7ffff7faa000     0x1000        0x0  ---p
      0x7ffff7faa000     0x7ffff7fae000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
      0x7ffff7fae000     0x7ffff7fb0000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so

So I am not seeing any advantage of this refactor: it slight increase the
number of syscalls for library loading and changes the 'debuggability' of
resulting shared library maps.

> @@ -98,7 +104,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 +129,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 +149,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)

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-29 13:17 ` Carlos O'Donell
  2023-03-29 13:26   ` stsp
@ 2023-03-29 17:03   ` stsp
  2023-03-29 18:13     ` Carlos O'Donell
  1 sibling, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-29 17:03 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha

[-- Attachment #1: Type: text/plain, Size: 861 bytes --]


29.03.2023 18:17, Carlos O'Donell пишет:
> On 3/18/23 12:50, Stas Sergeev via Libc-alpha wrote:
>
> A cover letter needs to explain in detail what the series does and why glibc
> should include the series e.g. use cases, workloads.
Attaching a cover letter for the upcoming
v10. The patch-set is not yet completed so
please let me know if the cover is now
adequate or needs more descriptions.

Also it would be good if we settle on this
"elf header" stuff before v10 is released,
so I am eagerly waiting your reply on whether
the proof was enough or not.
I can try to bring you any proof you want,
but I can't be asked in a way "its possible
to create an elf that can break your impl,
please prove the opposite". Proving that
something can't be created, usually requires
infinite amount of time. So any proof you
want, needs to be finite and well-defined.

[-- Attachment #2: 0000-cover-letter.patch --]
[-- Type: text/x-patch, Size: 3903 bytes --]

From 46e5095ebfe63be4dcd813c4237d6a491a3f9768 Mon Sep 17 00:00:00 2001
From: Stas Sergeev <stsp2@yandex.ru>
Date: Mon, 13 Feb 2023 18:15:34 +0500
Subject: [PATCH v10 0/12] implement dlmem() function

Changes in v10:
- squashed patch 1 as suggested by Adhemerval Zanella
- fixed a few bugs in an elf relocation machinery after various hot discussions
- added a new test tst-dlmem-dloff that demo-implements dlopen_with_offset()

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

This patch-set implements the dlmem() function that allows to load
the solib from page-aligned memory buffer. It has lots of optional
functionality for the fine-grained control over the loading process.
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

struct dlmem_args {
  /* Optional name to associate with the loaded object. */
  const char *soname;
  /* Namespace where to load the object. */
  Lmid_t nsid;
  /* dlmem-specific flags. */
  unsigned flags;
  /* Optional premap callback. */
  dlmem_premap_t *premap;
  /* Optional argument for premap callback. */
  void *cookie;
};

/* Like `dlmopen', but loads shared object from memory buffer.  */
extern void *dlmem (const unsigned char *buffer, size_t size, int mode,
		    struct dlmem_args *dlm_args);


In most cases dlm_args should just be set to NULL. It provides the
advanced functionality, most of which is obvious (soname, nsid).
The premap callback allows to set the relocation address for the solib.
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. Such functionality
may be interesting for virtualized environments where the relocation
address may have a special constraints, like eg MAP_32BIT.

-- 
2.37.2


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

* Re: [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx
  2023-03-18 16:51 ` [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx Stas Sergeev
@ 2023-03-29 17:10   ` Adhemerval Zanella Netto
  2023-03-30 16:08     ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 17:10 UTC (permalink / raw)
  To: Stas Sergeev, libc-alpha



On 18/03/23 13:51, Stas Sergeev via Libc-alpha wrote:
> It is not used in these functions.
> rtld.c:rtld_setup_main_map() does the same.
> 
> 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 | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index fcb39a78d4..ab8b648687 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -1379,10 +1379,10 @@ cannot enable executable stack as shared object requires");
>      switch (ph[-1].p_type)
>        {
>        case PT_NOTE:
> -	_dl_process_pt_note (l, fd, &ph[-1]);
> +	_dl_process_pt_note (l, -1, &ph[-1]);
>  	break;
>        case PT_GNU_PROPERTY:
> -	_dl_process_pt_gnu_property (l, fd, &ph[-1]);
> +	_dl_process_pt_gnu_property (l, -1, &ph[-1]);
>  	break;
>        }
>  


It allows both _dl_process_pt_note and _dl_process_pt_gnu_property to know
if the called where rtld code during statup code or dlopen.  But you are 
right that it is not used.

However this does not accomplish anything, a better refactor would to just
remove the argument altogether.  It at least would simplify the interface
and allow slight better code generation.

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

* Re: [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping
  2023-03-29 17:01   ` Adhemerval Zanella Netto
@ 2023-03-29 18:00     ` stsp
  2023-03-29 18:29       ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-29 18:00 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha


29.03.2023 22:01, Adhemerval Zanella Netto пишет:
> glibc code guidelines [1] suggest to explicit define the types, so
> 'unsigned int' here.

Fixed locally this and a few other
similar instances. Queued to v10.


> [1] https://sourceware.org/glibc/wiki/Style_and_Conventions
>
>> +
>> +#ifdef MAP_DENYWRITE
>> +  /* Tell mmap() that we are mapping solib. This flag enables things
>> +     like LD_PREFER_MAP_32BIT_EXEC. */
>> +  map_flags |= MAP_DENYWRITE;
>> +#endif
> Why do you need o add MAP_DENYWRITE? They are complete ignored by Linux,

But its not ignored in glibc, see

sysdeps/unix/sysv/linux/x86_64/64/mmap_internal.h

Without that flag PREFER_MAP_32BIT_EXEC
test fails.


> So basically it would add another mmap on program loading.  For instance, loading
> a simple empty main programs:

Yes, that's true.
Is this a problem?


> And it also slight change the mapping, using the same program:
>
> * Before:
>
>        0x7ffff7dc2000     0x7ffff7de8000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7de8000     0x7ffff7f54000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7f54000     0x7ffff7faa000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7faa000     0x7ffff7fab000     0x1000   0x1e8000  ---p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7fab000     0x7ffff7faf000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7faf000     0x7ffff7fb1000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>
> * With this patch:
>
>        0x7ffff7dc1000     0x7ffff7de7000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7de7000     0x7ffff7f53000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7f53000     0x7ffff7fa9000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7fa9000     0x7ffff7faa000     0x1000        0x0  ---p
>        0x7ffff7faa000     0x7ffff7fae000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>        0x7ffff7fae000     0x7ffff7fb0000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so

Mm, was staring on this for a while,
and file offsets and perms looks the
same. What differences do you mean
exactly?

> So I am not seeing any advantage of this refactor: it slight increase the
> number of syscalls for library loading and changes the 'debuggability' of
> resulting shared library maps.

This is not a "pure refactor", but a preparation
to dlmem(). dlmem() allows the user to pre-map
the memory area (optionally), so in the subsequent
patches I first call the user's callback, and if its
not there or doesn't want to pre-map, then I call
_dl_load_segment() for doing the same thing as
a fall-back.

Do you want me to squash also that patch
into the one that actually needs it?


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-29 17:03   ` stsp
@ 2023-03-29 18:13     ` Carlos O'Donell
  2023-03-29 18:29       ` stsp
  2023-03-31 11:04       ` stsp
  0 siblings, 2 replies; 107+ messages in thread
From: Carlos O'Donell @ 2023-03-29 18:13 UTC (permalink / raw)
  To: stsp, libc-alpha

On 3/29/23 13:03, stsp wrote:
> 
> 29.03.2023 18:17, Carlos O'Donell пишет:
>> On 3/18/23 12:50, Stas Sergeev via Libc-alpha wrote:
>>
>> A cover letter needs to explain in detail what the series does and why glibc
>> should include the series e.g. use cases, workloads.
> Attaching a cover letter for the upcoming
> v10. The patch-set is not yet completed so
> please let me know if the cover is now
> adequate or needs more descriptions.
> 
> Also it would be good if we settle on this
> "elf header" stuff before v10 is released,
> so I am eagerly waiting your reply on whether
> the proof was enough or not.
> I can try to bring you any proof you want,
> but I can't be asked in a way "its possible
> to create an elf that can break your impl,
> please prove the opposite". Proving that
> something can't be created, usually requires
> infinite amount of time. So any proof you
> want, needs to be finite and well-defined.

> From 46e5095ebfe63be4dcd813c4237d6a491a3f9768 Mon Sep 17 00:00:00 2001
> From: Stas Sergeev <stsp2@yandex.ru>
> Date: Mon, 13 Feb 2023 18:15:34 +0500
> Subject: [PATCH v10 0/12] implement dlmem() function

The most important thing is the reasons for the change and that should come first.

> Changes in v10:
> - squashed patch 1 as suggested by Adhemerval Zanella
> - fixed a few bugs in an elf relocation machinery after various hot discussions
> - added a new test tst-dlmem-dloff that demo-implements dlopen_with_offset()
> 
> 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
> 
> This patch-set implements the dlmem() function that allows to load
> the solib from page-aligned memory buffer. It has lots of optional
> functionality for the fine-grained control over the loading process.
> The API looks as below:

This needs to explain the workload that requires the API.

Why that workload is generic.

Any core system library, like glibc, is not the place for *all* APIs, but the
place for the most generic building blocks for ISO C, POSIX, BSD, GNU, and
Linux APIs.
 
> /* 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
> 
> struct dlmem_args {
>   /* Optional name to associate with the loaded object. */
>   const char *soname;
>   /* Namespace where to load the object. */
>   Lmid_t nsid;
>   /* dlmem-specific flags. */
>   unsigned flags;
>   /* Optional premap callback. */
>   dlmem_premap_t *premap;
>   /* Optional argument for premap callback. */
>   void *cookie;
> };
> 
> /* Like `dlmopen', but loads shared object from memory buffer.  */
> extern void *dlmem (const unsigned char *buffer, size_t size, int mode,
> 		    struct dlmem_args *dlm_args);
> 
> 
> In most cases dlm_args should just be set to NULL. It provides the
> advanced functionality, most of which is obvious (soname, nsid).
> The premap callback allows to set the relocation address for the solib.
> 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. Such functionality
> may be interesting for virtualized environments where the relocation
> address may have a special constraints, like eg MAP_32BIT.

It is not that the workload you have is unimportant, since importance is relative,
but the workload is not generic enough for glibc to add dlmem() into the library.

You need to have a very strong argument for the question "Why should this go into
the library?"
 
> -- 
> 2.37.2
> 


-- 
Cheers,
Carlos.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-29 18:13     ` Carlos O'Donell
@ 2023-03-29 18:29       ` stsp
  2023-03-31 11:04       ` stsp
  1 sibling, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-29 18:29 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha


29.03.2023 23:13, Carlos O'Donell пишет:
> It is not that the workload you have is unimportant, since importance is relative,
> but the workload is not generic enough for glibc to add dlmem() into the library.
>
> You need to have a very strong argument for the question "Why should this go into
> the library?"
I'll write that down to the cover-letter,
but I'd like to also summarize it here to
get your opinion before posting the patch.

My view on that picture is that dlmem()
is a very generic API that allows to implement
eg dlopen_with_offset() in a dozen of lines.
And in that case - quite importantly -
no "advanced" features are even used, so
the last arg is just NULL.

Now the advanced features offered by the
last argument, are indeed quite specific to
my not-so-common use-case. So my idea
is exactly that: implement what other ppl
asked before, make it powerful and generic,
and then sneak in an optional pointer arg
for myself. :)
Is it a viable plan?
I also tried to "hide" the me-specific functionality
in an auditor - that plan was rejected as
non-viable. Optional pointer arg looks a
bit better to me. :)

So I am trying to put this not as "I want
to implement the function solely for myself",
but as "please let me have an optional
pointer arg in a very generic and powerful
function". :)


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

* Re: [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping
  2023-03-29 18:00     ` stsp
@ 2023-03-29 18:29       ` Adhemerval Zanella Netto
  2023-03-29 18:46         ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 18:29 UTC (permalink / raw)
  To: stsp, libc-alpha



On 29/03/23 15:00, stsp wrote:
> 
> 29.03.2023 22:01, Adhemerval Zanella Netto пишет:
>> glibc code guidelines [1] suggest to explicit define the types, so
>> 'unsigned int' here.
> 
> Fixed locally this and a few other
> similar instances. Queued to v10.
> 
> 
>> [1] https://sourceware.org/glibc/wiki/Style_and_Conventions
>>
>>> +
>>> +#ifdef MAP_DENYWRITE
>>> +  /* Tell mmap() that we are mapping solib. This flag enables things
>>> +     like LD_PREFER_MAP_32BIT_EXEC. */
>>> +  map_flags |= MAP_DENYWRITE;
>>> +#endif
>> Why do you need o add MAP_DENYWRITE? They are complete ignored by Linux,
> 
> But its not ignored in glibc, see
> 
> sysdeps/unix/sysv/linux/x86_64/64/mmap_internal.h
> 
> Without that flag PREFER_MAP_32BIT_EXEC
> test fails.

Because elf/dl-load.h already defines MAP_COPY that handles it, so why
not use it instead?

> 
> 
>> So basically it would add another mmap on program loading.  For instance, loading
>> a simple empty main programs:
> 
> Yes, that's true.
> Is this a problem?
> 
> 

Yes, Linux limits a maximum mmap both per process [1].  This code increase both
the total mapping requires and runtime cost to setup a new shared library.

[1] https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/sysctl/vm.rst#max_map_count


>> And it also slight change the mapping, using the same program:
>>
>> * Before:
>>
>>        0x7ffff7dc2000     0x7ffff7de8000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7de8000     0x7ffff7f54000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7f54000     0x7ffff7faa000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7faa000     0x7ffff7fab000     0x1000   0x1e8000  ---p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7fab000     0x7ffff7faf000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7faf000     0x7ffff7fb1000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>
>> * With this patch:
>>
>>        0x7ffff7dc1000     0x7ffff7de7000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7de7000     0x7ffff7f53000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7f53000     0x7ffff7fa9000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7fa9000     0x7ffff7faa000     0x1000        0x0  ---p
>>        0x7ffff7faa000     0x7ffff7fae000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>        0x7ffff7fae000     0x7ffff7fb0000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
> 
> Mm, was staring on this for a while,
> and file offsets and perms looks the
> same. What differences do you mean
> exactly?

The PROT_NONE mapping now does not have a file associated:

  0x7ffff7fa9000     0x7ffff7faa000     0x1000        0x0  ---p

This is not a problem itself, but again this change decrease the information
that some tools might use to analyze the memory mapping.

> 
>> So I am not seeing any advantage of this refactor: it slight increase the
>> number of syscalls for library loading and changes the 'debuggability' of
>> resulting shared library maps.
> 
> This is not a "pure refactor", but a preparation
> to dlmem(). dlmem() allows the user to pre-map
> the memory area (optionally), so in the subsequent
> patches I first call the user's callback, and if its
> not there or doesn't want to pre-map, then I call
> _dl_load_segment() for doing the same thing as
> a fall-back.
> 
> Do you want me to squash also that patch
> into the one that actually needs it?
> 

No need, this patch as-is is nack.  This patch has only cons: it increases runtime
cost for generic case, increase the minimum number of mmap segment a shared library
required, and decrease debuggability.

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

* Re: [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping
  2023-03-29 18:29       ` Adhemerval Zanella Netto
@ 2023-03-29 18:46         ` stsp
  2023-03-29 19:17           ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-29 18:46 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha


29.03.2023 23:29, Adhemerval Zanella Netto пишет:
> On 29/03/23 15:00, stsp wrote:
>> But its not ignored in glibc, see
>>
>> sysdeps/unix/sysv/linux/x86_64/64/mmap_internal.h
>>
>> Without that flag PREFER_MAP_32BIT_EXEC
>> test fails.
> Because elf/dl-load.h already defines MAP_COPY that handles it, so why
> not use it instead?

Would MAP_COPY be a good choice for
explicitly anonymous mapping? If so -
can change.

>>> So basically it would add another mmap on program loading.  For instance, loading
>>> a simple empty main programs:
>> Yes, that's true.
>> Is this a problem?
>>
>>
> Yes, Linux limits a maximum mmap both per process [1].  This code increase both
> the total mapping requires and runtime cost to setup a new shared library.
>
> [1] https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/sysctl/vm.rst#max_map_count

This talks about the map areas.
I don't think map areas number changed.
Extra syscall - yes. Extra map area - no.
So I don't think my patch is a subject of
the aforementioned system limit.

>>> And it also slight change the mapping, using the same program:
>>>
>>> * Before:
>>>
>>>         0x7ffff7dc2000     0x7ffff7de8000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7de8000     0x7ffff7f54000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7f54000     0x7ffff7faa000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7faa000     0x7ffff7fab000     0x1000   0x1e8000  ---p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7fab000     0x7ffff7faf000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7faf000     0x7ffff7fb1000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>
>>> * With this patch:
>>>
>>>         0x7ffff7dc1000     0x7ffff7de7000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7de7000     0x7ffff7f53000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7f53000     0x7ffff7fa9000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7fa9000     0x7ffff7faa000     0x1000        0x0  ---p
>>>         0x7ffff7faa000     0x7ffff7fae000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>         0x7ffff7fae000     0x7ffff7fb0000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>> Mm, was staring on this for a while,
>> and file offsets and perms looks the
>> same. What differences do you mean
>> exactly?
> The PROT_NONE mapping now does not have a file associated:
>
>    0x7ffff7fa9000     0x7ffff7faa000     0x1000        0x0  ---p
>
> This is not a problem itself, but again this change decrease the information
> that some tools might use to analyze the memory mapping.

Ah, that seems to be a "hole" are
between segments. I actually think
my handling is much better. Without
my patch, such holes are filled with
actually the _random_ page from the
original file mapping. Just whatever
page happened to have that offset.
Do you think the random page from
the file is a good idea for tooling/debugging?


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

* Re: [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping
  2023-03-29 18:46         ` stsp
@ 2023-03-29 19:17           ` Adhemerval Zanella Netto
  2023-03-29 19:43             ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-29 19:17 UTC (permalink / raw)
  To: stsp, libc-alpha



On 29/03/23 15:46, stsp wrote:
> 
> 29.03.2023 23:29, Adhemerval Zanella Netto пишет:
>> On 29/03/23 15:00, stsp wrote:
>>> But its not ignored in glibc, see
>>>
>>> sysdeps/unix/sysv/linux/x86_64/64/mmap_internal.h
>>>
>>> Without that flag PREFER_MAP_32BIT_EXEC
>>> test fails.
>> Because elf/dl-load.h already defines MAP_COPY that handles it, so why
>> not use it instead?
> 
> Would MAP_COPY be a good choice for
> explicitly anonymous mapping? If so -
> can change.

It avoid code duplication.

> 
>>>> So basically it would add another mmap on program loading.  For instance, loading
>>>> a simple empty main programs:
>>> Yes, that's true.
>>> Is this a problem?
>>>
>>>
>> Yes, Linux limits a maximum mmap both per process [1].  This code increase both
>> the total mapping requires and runtime cost to setup a new shared library.
>>
>> [1] https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/sysctl/vm.rst#max_map_count
> 
> This talks about the map areas.
> I don't think map areas number changed.
> Extra syscall - yes. Extra map area - no.
> So I don't think my patch is a subject of
> the aforementioned system limit.

In fact I think this is gdb limitation, accessing the procfs directly there is
no extra mapping and all the segments are indeed mapped by associated shared
libraries.

> 
>>>> And it also slight change the mapping, using the same program:
>>>>
>>>> * Before:
>>>>
>>>>         0x7ffff7dc2000     0x7ffff7de8000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7de8000     0x7ffff7f54000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7f54000     0x7ffff7faa000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7faa000     0x7ffff7fab000     0x1000   0x1e8000  ---p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7fab000     0x7ffff7faf000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7faf000     0x7ffff7fb1000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>
>>>> * With this patch:
>>>>
>>>>         0x7ffff7dc1000     0x7ffff7de7000    0x26000        0x0  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7de7000     0x7ffff7f53000   0x16c000    0x26000  r-xp   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7f53000     0x7ffff7fa9000    0x56000   0x192000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7fa9000     0x7ffff7faa000     0x1000        0x0  ---p
>>>>         0x7ffff7faa000     0x7ffff7fae000     0x4000   0x1e8000  r--p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>>>         0x7ffff7fae000     0x7ffff7fb0000     0x2000   0x1ec000  rw-p   /home/azanella/Projects/glibc/build/x86_64-linux-gnu/libc.so
>>> Mm, was staring on this for a while,
>>> and file offsets and perms looks the
>>> same. What differences do you mean
>>> exactly?
>> The PROT_NONE mapping now does not have a file associated:
>>
>>    0x7ffff7fa9000     0x7ffff7faa000     0x1000        0x0  ---p
>>
>> This is not a problem itself, but again this change decrease the information
>> that some tools might use to analyze the memory mapping.
> 
> Ah, that seems to be a "hole" are
> between segments. I actually think
> my handling is much better. Without
> my patch, such holes are filled with
> actually the _random_ page from the
> original file mapping. Just whatever
> page happened to have that offset.
> Do you think the random page from
> the file is a good idea for tooling/debugging?

It seems to be a gdb limitation that is showing some wrong information.  But again,
I really don't see *why* this change is needed: the current algorithms already
maps the ELF segments correctly and have random data on the hole does not really
matter (it would be mapped as PROT_NONE).

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

* Re: [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping
  2023-03-29 19:17           ` Adhemerval Zanella Netto
@ 2023-03-29 19:43             ` stsp
  0 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-29 19:43 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha


30.03.2023 00:17, Adhemerval Zanella Netto пишет:
> On 29/03/23 15:46, stsp wrote:
>
>> Would MAP_COPY be a good choice for
>> explicitly anonymous mapping? If so -
>> can change.
> It avoid code duplication.

Queued for v10.

>>
>> Ah, that seems to be a "hole" are
>> between segments. I actually think
>> my handling is much better. Without
>> my patch, such holes are filled with
>> actually the _random_ page from the
>> original file mapping. Just whatever
>> page happened to have that offset.
>> Do you think the random page from
>> the file is a good idea for tooling/debugging?
> It seems to be a gdb limitation that is showing some wrong information.  But again,
> I really don't see *why* this change is needed: the current algorithms already
> maps the ELF segments correctly and have random data on the hole does not really
> matter (it would be mapped as PROT_NONE).
Its only for dlmem().
In dlmem() you need to do anonymous
mmap at the relocation address, and
after that we shouldn't skip the first
segment.
I can remove this patch if you insist, but
the cost will likely be quite high, as I'll
need then to propagate some flag up to
_dl_map_segments() to tell if we can
skip the first segment or not, and if
we should call _dl_map_segment() or
a premap callback.

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

* Re: [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767]
  2023-03-29 14:10       ` Jonathon Anderson
  2023-03-29 14:20         ` stsp
@ 2023-03-30  8:09         ` stsp
  1 sibling, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-30  8:09 UTC (permalink / raw)
  To: Jonathon Anderson, Carlos O'Donell, libc-alpha


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

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

* Re: [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx
  2023-03-29 17:10   ` Adhemerval Zanella Netto
@ 2023-03-30 16:08     ` stsp
  2023-03-30 20:46       ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-30 16:08 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, libc-alpha


29.03.2023 22:10, Adhemerval Zanella Netto пишет:
>
> On 18/03/23 13:51, Stas Sergeev via Libc-alpha wrote:
>> It is not used in these functions.
>> rtld.c:rtld_setup_main_map() does the same.
>>
>> 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 | 4 ++--
>>   1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/elf/dl-load.c b/elf/dl-load.c
>> index fcb39a78d4..ab8b648687 100644
>> --- a/elf/dl-load.c
>> +++ b/elf/dl-load.c
>> @@ -1379,10 +1379,10 @@ cannot enable executable stack as shared object requires");
>>       switch (ph[-1].p_type)
>>         {
>>         case PT_NOTE:
>> -	_dl_process_pt_note (l, fd, &ph[-1]);
>> +	_dl_process_pt_note (l, -1, &ph[-1]);
>>   	break;
>>         case PT_GNU_PROPERTY:
>> -	_dl_process_pt_gnu_property (l, fd, &ph[-1]);
>> +	_dl_process_pt_gnu_property (l, -1, &ph[-1]);
>>   	break;
>>         }
>>   
>
> It allows both _dl_process_pt_note and _dl_process_pt_gnu_property to know
> if the called where rtld code during statup code or dlopen.  But you are
> right that it is not used.
>
> However this does not accomplish anything, a better refactor would to just
> remove the argument altogether.  It at least would simplify the interface
> and allow slight better code generation.

I tried to do that, but there is also that
_dl_process_gnu_property() called from
_dl_process_pt_gnu_property().
For aarch64 it has this:
       unsigned int feature_1 = *(unsigned int *) data;
       if (feature_1 & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
         _dl_bti_protect (l, fd);

What should I do here to remove that fd?


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

* Re: [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx
  2023-03-30 16:08     ` stsp
@ 2023-03-30 20:46       ` Adhemerval Zanella Netto
  2023-03-31 12:02         ` Szabolcs Nagy
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-30 20:46 UTC (permalink / raw)
  To: stsp, libc-alpha, Szabolcs Nagy



On 30/03/23 13:08, stsp wrote:
> 
> 29.03.2023 22:10, Adhemerval Zanella Netto пишет:
>>
>> On 18/03/23 13:51, Stas Sergeev via Libc-alpha wrote:
>>> It is not used in these functions.
>>> rtld.c:rtld_setup_main_map() does the same.
>>>
>>> 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 | 4 ++--
>>>   1 file changed, 2 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/elf/dl-load.c b/elf/dl-load.c
>>> index fcb39a78d4..ab8b648687 100644
>>> --- a/elf/dl-load.c
>>> +++ b/elf/dl-load.c
>>> @@ -1379,10 +1379,10 @@ cannot enable executable stack as shared object requires");
>>>       switch (ph[-1].p_type)
>>>         {
>>>         case PT_NOTE:
>>> -    _dl_process_pt_note (l, fd, &ph[-1]);
>>> +    _dl_process_pt_note (l, -1, &ph[-1]);
>>>       break;
>>>         case PT_GNU_PROPERTY:
>>> -    _dl_process_pt_gnu_property (l, fd, &ph[-1]);
>>> +    _dl_process_pt_gnu_property (l, -1, &ph[-1]);
>>>       break;
>>>         }
>>>   
>>
>> It allows both _dl_process_pt_note and _dl_process_pt_gnu_property to know
>> if the called where rtld code during statup code or dlopen.  But you are
>> right that it is not used.
>>
>> However this does not accomplish anything, a better refactor would to just
>> remove the argument altogether.  It at least would simplify the interface
>> and allow slight better code generation.
> 
> I tried to do that, but there is also that
> _dl_process_gnu_property() called from
> _dl_process_pt_gnu_property().
> For aarch64 it has this:
>       unsigned int feature_1 = *(unsigned int *) data;
>       if (feature_1 & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
>         _dl_bti_protect (l, fd);
> 
> What should I do here to remove that fd?
> 

In fact aarch64 _dl_bti_protect requires to know whether the map was done by 
the kernel or not.

Szabolcs, shouldn't the code:

1257   for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph)
1258     switch (ph[-1].p_type)
1259       {
1260       case PT_NOTE:
1261         _dl_process_pt_note (main_map, -1, &ph[-1]);
1262         break;
1263       case PT_GNU_PROPERTY:
1264         _dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
1265         break;
1266       }

Take in consideration whether the main_map was allocated by the kernel or
by loader to pass the correct value on 'fd'?  

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-29 18:13     ` Carlos O'Donell
  2023-03-29 18:29       ` stsp
@ 2023-03-31 11:04       ` stsp
  2023-04-13 21:17         ` Carlos O'Donell
  1 sibling, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-31 11:04 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha, Jonathon Anderson

[-- Attachment #1: Type: text/plain, Size: 843 bytes --]

Hi Carlos, Jonathon.

29.03.2023 23:13, Carlos O'Donell пишет:
> The most important thing is the reasons for the change and that should come first.

Done, please see the attachment.

> This needs to explain the workload that requires the API.
>
> Why that workload is generic.
>
> Any core system library, like glibc, is not the place for *all* APIs, but the
> place for the most generic building blocks for ISO C, POSIX, BSD, GNU, and
> Linux APIs.

I hope I've got that suggestion rightly.
Please see if my current description is
adequate.

And on the other front...
I studied and documented (in the attachment)
all the cases where my impl fails to arrange
an elf segments properly... I have to admit
such cases were possible. :(
I documented them and their mitigations in
the "Limitations" section.
Let me know if this is now adequate.

[-- Attachment #2: 0000-cover-letter.patch --]
[-- Type: text/x-patch, Size: 7080 bytes --]

From 46e5095ebfe63be4dcd813c4237d6a491a3f9768 Mon Sep 17 00:00:00 2001
From: Stas Sergeev <stsp2@yandex.ru>
Date: Mon, 13 Feb 2023 18:15:34 +0500
Subject: [PATCH v10 0/12] implement dlmem() function

This patch-set implements the dlmem() function that allows to load
the solib from page-aligned memory buffer. It 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.
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

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


In most cases dlm_args should just be set to NULL. It provides the
advanced functionality, most of which is obvious (soname, nsid).
The premap callback allows to set the relocation address for the solib.
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.

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_DONTREPLACE 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_DONTREPLACE 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
- 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

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] 107+ messages in thread

* Re: [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx
  2023-03-30 20:46       ` Adhemerval Zanella Netto
@ 2023-03-31 12:02         ` Szabolcs Nagy
  2023-03-31 12:54           ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 107+ messages in thread
From: Szabolcs Nagy @ 2023-03-31 12:02 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, stsp, libc-alpha

The 03/30/2023 17:46, Adhemerval Zanella Netto wrote:
> On 30/03/23 13:08, stsp wrote:
> > 
> > 29.03.2023 22:10, Adhemerval Zanella Netto пишет:
> >>
> >> On 18/03/23 13:51, Stas Sergeev via Libc-alpha wrote:
> >>> It is not used in these functions.
> >>> rtld.c:rtld_setup_main_map() does the same.
> >>>
> >>> 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 | 4 ++--
> >>>   1 file changed, 2 insertions(+), 2 deletions(-)
> >>>
> >>> diff --git a/elf/dl-load.c b/elf/dl-load.c
> >>> index fcb39a78d4..ab8b648687 100644
> >>> --- a/elf/dl-load.c
> >>> +++ b/elf/dl-load.c
> >>> @@ -1379,10 +1379,10 @@ cannot enable executable stack as shared object requires");
> >>>       switch (ph[-1].p_type)
> >>>         {
> >>>         case PT_NOTE:
> >>> -    _dl_process_pt_note (l, fd, &ph[-1]);
> >>> +    _dl_process_pt_note (l, -1, &ph[-1]);
> >>>       break;
> >>>         case PT_GNU_PROPERTY:
> >>> -    _dl_process_pt_gnu_property (l, fd, &ph[-1]);
> >>> +    _dl_process_pt_gnu_property (l, -1, &ph[-1]);
> >>>       break;
> >>>         }
> >>>   
> >>
> >> It allows both _dl_process_pt_note and _dl_process_pt_gnu_property to know
> >> if the called where rtld code during statup code or dlopen.  But you are
> >> right that it is not used.
> >>
> >> However this does not accomplish anything, a better refactor would to just
> >> remove the argument altogether.  It at least would simplify the interface
> >> and allow slight better code generation.
> > 
> > I tried to do that, but there is also that
> > _dl_process_gnu_property() called from
> > _dl_process_pt_gnu_property().
> > For aarch64 it has this:
> >       unsigned int feature_1 = *(unsigned int *) data;
> >       if (feature_1 & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
> >         _dl_bti_protect (l, fd);
> > 
> > What should I do here to remove that fd?
> > 
> 
> In fact aarch64 _dl_bti_protect requires to know whether the map was done by 
> the kernel or not.
> 
> Szabolcs, shouldn't the code:
> 
> 1257   for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph)
> 1258     switch (ph[-1].p_type)
> 1259       {
> 1260       case PT_NOTE:
> 1261         _dl_process_pt_note (main_map, -1, &ph[-1]);
> 1262         break;
> 1263       case PT_GNU_PROPERTY:
> 1264         _dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
> 1265         break;
> 1266       }
> 
> Take in consideration whether the main_map was allocated by the kernel or
> by loader to pass the correct value on 'fd'?  

if the exe is loaded by ld.so then we already took care of bti
(and other gnu properties) at load time.

here we could skip processing properties again in that case but
it does not hurt on aarch64 (it will try to mprotect with
PROT_BTI again and ignore any failures).

however the fd must be passed down in elf/dl-load.c otherwise
bti protection is silently dropped (when systemd filters out
mprotect with PROT_EXEC, this is being fixed, but there are old
systems configured like that).


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-29 13:18   ` stsp
@ 2023-03-31 12:20     ` Szabolcs Nagy
  2023-03-31 13:51       ` stsp
                         ` (2 more replies)
  0 siblings, 3 replies; 107+ messages in thread
From: Szabolcs Nagy @ 2023-03-31 12:20 UTC (permalink / raw)
  To: stsp, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

The 03/29/2023 18:18, stsp via Libc-alpha wrote:
> Just to be more constructive, here is the
> example:
> 
> void *dlopen_with_offset(const char *file, off_t offset, int flags)
> {

this api requires no libc change:

dump the contents to a temp file and use dlopen on it.


>     off_t len;
>     void *addr;
>     void *handle;
>     int fd;
> 
>     fd = open(file, O_RDONLY);
>     if (fd == -1)
>         return NULL;
>     len = lseek(fd, 0, SEEK_END);
>     lseek(fd, 0, SEEK_SET);
>     if (len <= offset)
>       goto err_close;
>     len -= offset;
>     /* if offset unaligned then just use read() */
>     if (offset & (PAGE_SIZE - 1)) {
>         int rd;
>         addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
>                     MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
>         if (addr == MAP_FAILED)
>             goto err_close;
>         rd = read(fd, addr, len);
>         if (rd != len) {
>             munmap(addr, len);
>             goto err_close;
>         }
>     } else {
>         addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, offset);
>     }
>     close(fd);
>     if (addr == MAP_FAILED)
>         return NULL;
>     handle = dlmem(addr, len, flags, NULL);
>     munmap(addr, len);
>     return handle;
> 
> err_close:
>     close(fd);
>     return NULL;
> }
> 
> 
> Does it parse an elf headers?
> If so - where?

it creates a mapping that's not compatible with dynamic loading
requirements *depending on* what the elf headers say.
(elf segments may be loaded at various offsets that can conflict
with the mapping that you just made)

but i'm not going to think about all the different ways this
is broken: you cut across abstraction layers exposing dynamic
loading internals where a temp file works fine. if there is
a performance problem (e.g. page sharing) likely that can be
solved too via kernel level apis, not ld.so.

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

* Re: [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx
  2023-03-31 12:02         ` Szabolcs Nagy
@ 2023-03-31 12:54           ` Adhemerval Zanella Netto
  2023-03-31 14:04             ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-03-31 12:54 UTC (permalink / raw)
  To: Szabolcs Nagy, stsp, libc-alpha



On 31/03/23 09:02, Szabolcs Nagy wrote:
> The 03/30/2023 17:46, Adhemerval Zanella Netto wrote:
>> On 30/03/23 13:08, stsp wrote:
>>>
>>> 29.03.2023 22:10, Adhemerval Zanella Netto пишет:
>>>>
>>>> On 18/03/23 13:51, Stas Sergeev via Libc-alpha wrote:
>>>>> It is not used in these functions.
>>>>> rtld.c:rtld_setup_main_map() does the same.
>>>>>
>>>>> 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 | 4 ++--
>>>>>   1 file changed, 2 insertions(+), 2 deletions(-)
>>>>>
>>>>> diff --git a/elf/dl-load.c b/elf/dl-load.c
>>>>> index fcb39a78d4..ab8b648687 100644
>>>>> --- a/elf/dl-load.c
>>>>> +++ b/elf/dl-load.c
>>>>> @@ -1379,10 +1379,10 @@ cannot enable executable stack as shared object requires");
>>>>>       switch (ph[-1].p_type)
>>>>>         {
>>>>>         case PT_NOTE:
>>>>> -    _dl_process_pt_note (l, fd, &ph[-1]);
>>>>> +    _dl_process_pt_note (l, -1, &ph[-1]);
>>>>>       break;
>>>>>         case PT_GNU_PROPERTY:
>>>>> -    _dl_process_pt_gnu_property (l, fd, &ph[-1]);
>>>>> +    _dl_process_pt_gnu_property (l, -1, &ph[-1]);
>>>>>       break;
>>>>>         }
>>>>>   
>>>>
>>>> It allows both _dl_process_pt_note and _dl_process_pt_gnu_property to know
>>>> if the called where rtld code during statup code or dlopen.  But you are
>>>> right that it is not used.
>>>>
>>>> However this does not accomplish anything, a better refactor would to just
>>>> remove the argument altogether.  It at least would simplify the interface
>>>> and allow slight better code generation.
>>>
>>> I tried to do that, but there is also that
>>> _dl_process_gnu_property() called from
>>> _dl_process_pt_gnu_property().
>>> For aarch64 it has this:
>>>       unsigned int feature_1 = *(unsigned int *) data;
>>>       if (feature_1 & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
>>>         _dl_bti_protect (l, fd);
>>>
>>> What should I do here to remove that fd?
>>>
>>
>> In fact aarch64 _dl_bti_protect requires to know whether the map was done by 
>> the kernel or not.
>>
>> Szabolcs, shouldn't the code:
>>
>> 1257   for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph)
>> 1258     switch (ph[-1].p_type)
>> 1259       {
>> 1260       case PT_NOTE:
>> 1261         _dl_process_pt_note (main_map, -1, &ph[-1]);
>> 1262         break;
>> 1263       case PT_GNU_PROPERTY:
>> 1264         _dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
>> 1265         break;
>> 1266       }
>>
>> Take in consideration whether the main_map was allocated by the kernel or
>> by loader to pass the correct value on 'fd'?  
> 
> if the exe is loaded by ld.so then we already took care of bti
> (and other gnu properties) at load time.
> 
> here we could skip processing properties again in that case but
> it does not hurt on aarch64 (it will try to mprotect with
> PROT_BTI again and ignore any failures).

Right, I was asking if this part is really necessary.  Because if kernel
already supports BTI this will be covered, otherwise this seems unnecessary.

> 
> however the fd must be passed down in elf/dl-load.c otherwise
> bti protection is silently dropped (when systemd filters out
> mprotect with PROT_EXEC, this is being fixed, but there are old
> systems configured like that).
> 

This patch I figured out much.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 12:20     ` Szabolcs Nagy
@ 2023-03-31 13:51       ` stsp
  2023-03-31 14:49         ` Rich Felker
  2023-03-31 14:44       ` stsp
  2023-03-31 15:12       ` stsp
  2 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-31 13:51 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Hi Szabolcs, thanks for a follow-up.
Unfortunately I need to bypass your
comment, as all these claims were
1000 times refuted already on that
ML and also here:
https://sourceware.org/bugzilla/show_bug.cgi?id=30007

I'd be glad if you ask a concrete questions.
I want to answer and discuss things with
you or anyone else. Except that the "walking
in circles forever" scenario must be avoided.


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

* Re: [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx
  2023-03-31 12:54           ` Adhemerval Zanella Netto
@ 2023-03-31 14:04             ` stsp
  0 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-31 14:04 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, libc-alpha

Hi Adhemerval,

31.03.2023 17:54, Adhemerval Zanella Netto пишет:
>> however the fd must be passed down in elf/dl-load.c otherwise
>> bti protection is silently dropped (when systemd filters out
>> mprotect with PROT_EXEC, this is being fixed, but there are old
>> systems configured like that).
>>
> This patch I figured out much.
So if I understand correctly, these fds
should stay, right?
If so - may I bypass the _dl_process_pt_XX
calls completely in case of dlmem, or
are they needed for dlmem?
Sorry I have no idea what they do...

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 12:20     ` Szabolcs Nagy
  2023-03-31 13:51       ` stsp
@ 2023-03-31 14:44       ` stsp
  2023-03-31 15:12       ` stsp
  2 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-31 14:44 UTC (permalink / raw)
  To: Carlos O'Donell
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Rich Felker

Hi Carlos.

The last letter from Szabolcs and a few
private e-mails I've got, suggest that this
"elf parsing" argument keeps raging in.

Carlos, you are the only person here who
asked me to _prove_ the problem is not
there, others just keep throwing it to me.

Carlos, you are the only person to whom
I presented such a proof. As the result, I
really need your answer on whether the
proof was satisfactory or not. Or if you
need some other proof - I need to know
about that. Getting rid of that "elf parsing"
argument seems to be possible only if you,
Carlos, permit that to happen. Otherwise
it doesn't matter what I do, I'll be always
thrown with that argument, and then we
can't progress...

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 13:51       ` stsp
@ 2023-03-31 14:49         ` Rich Felker
  2023-03-31 14:56           ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Rich Felker @ 2023-03-31 14:49 UTC (permalink / raw)
  To: stsp
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell

On Fri, Mar 31, 2023 at 06:51:24PM +0500, stsp wrote:
> Hi Szabolcs, thanks for a follow-up.
> Unfortunately I need to bypass your
> comment, as all these claims were
> 1000 times refuted already on that
> ML and also here:
> https://sourceware.org/bugzilla/show_bug.cgi?id=30007
> 
> I'd be glad if you ask a concrete questions.
> I want to answer and discuss things with
> you or anyone else. Except that the "walking
> in circles forever" scenario must be avoided.

The "walking in circles forever scenario" can be avoided by dropping
this waste of everybody's time. You don't get to shoehorn pet
functionality into libc.

Rich

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 14:49         ` Rich Felker
@ 2023-03-31 14:56           ` stsp
  2023-03-31 14:58             ` Rich Felker
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-31 14:56 UTC (permalink / raw)
  To: Rich Felker
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell

[-- Attachment #1: Type: text/plain, Size: 863 bytes --]


31.03.2023 19:49, Rich Felker пишет:
> The "walking in circles forever scenario" can be avoided by dropping
> this waste of everybody's time. You don't get to shoehorn pet
> functionality into libc.

Thanks.
I attached the detailed description of the
API. Maybe if you read that, your statements
would be more concrete.

And no, this API doesn't require elf header
parsing by user, or anything else alike.
It always lays out an elf image per vaddr's.
I demonstrated that to Carlos, so if he
permits this false claim about my patch
to go away, then the argument would
disappear.

And no, it can't be replaced by memfd and
alike techniques - I proved that many times,
and at least Jonathon accepted that fact
after a month of discussions.

Now if I can convince some individuals in
some parts of the mosaic, I have no idea
how to convince everyone at once...

[-- Attachment #2: 0000-cover-letter.patch --]
[-- Type: text/x-patch, Size: 7080 bytes --]

From 46e5095ebfe63be4dcd813c4237d6a491a3f9768 Mon Sep 17 00:00:00 2001
From: Stas Sergeev <stsp2@yandex.ru>
Date: Mon, 13 Feb 2023 18:15:34 +0500
Subject: [PATCH v10 0/12] implement dlmem() function

This patch-set implements the dlmem() function that allows to load
the solib from page-aligned memory buffer. It 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.
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

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


In most cases dlm_args should just be set to NULL. It provides the
advanced functionality, most of which is obvious (soname, nsid).
The premap callback allows to set the relocation address for the solib.
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.

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_DONTREPLACE 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_DONTREPLACE 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
- 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

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] 107+ messages in thread

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 14:56           ` stsp
@ 2023-03-31 14:58             ` Rich Felker
  2023-03-31 15:03               ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Rich Felker @ 2023-03-31 14:58 UTC (permalink / raw)
  To: stsp
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell

On Fri, Mar 31, 2023 at 07:56:41PM +0500, stsp wrote:
> 
> 31.03.2023 19:49, Rich Felker пишет:
> >The "walking in circles forever scenario" can be avoided by dropping
> >this waste of everybody's time. You don't get to shoehorn pet
> >functionality into libc.
> 
> Thanks.
> I attached the detailed description of the
> API. Maybe if you read that, your statements
> would be more concrete.
> 
> And no, this API doesn't require elf header
> parsing by user, or anything else alike.
> It always lays out an elf image per vaddr's.
> I demonstrated that to Carlos, so if he
> permits this false claim about my patch
> to go away, then the argument would
> disappear.
> 
> And no, it can't be replaced by memfd and
> alike techniques - I proved that many times,
> and at least Jonathon accepted that fact
> after a month of discussions.
> 
> Now if I can convince some individuals in
> some parts of the mosaic, I have no idea
> how to convince everyone at once...

I'm not going to read this any more than I'm going to watch a YouTube
video about free energy. It's been rehashed over and over why this
does not work. Please just stop.

Rich

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 14:58             ` Rich Felker
@ 2023-03-31 15:03               ` stsp
  0 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-31 15:03 UTC (permalink / raw)
  To: Rich Felker
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell


31.03.2023 19:58, Rich Felker пишет:
> I'm not going to read this any more than I'm going to watch a YouTube
> video about free energy. It's been rehashed over and over why this
> does not work. Please just stop.


Sorry but it works and lays out an
elf segments properly. My test-case has
a proof of that:


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


If it didn't work, there would be only
1 reference to the solib. But after dlmem,
in this test-case there are 5. This proves
that the elf was laid out correctly per
vaddr's.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 12:20     ` Szabolcs Nagy
  2023-03-31 13:51       ` stsp
  2023-03-31 14:44       ` stsp
@ 2023-03-31 15:12       ` stsp
  2023-03-31 17:12         ` Szabolcs Nagy
  2 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-31 15:12 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Hi Szabolcs, I need to try again with answering
this.

31.03.2023 17:20, Szabolcs Nagy пишет:
> The 03/29/2023 18:18, stsp via Libc-alpha wrote:
>> Just to be more constructive, here is the
>> example:
>>
>> void *dlopen_with_offset(const char *file, off_t offset, int flags)
>> {
> this api requires no libc change:
>
> dump the contents to a temp file and use dlopen on it.

This doesn't work for my use-case.
dlmem() can optionally preserve the destination
mapping, which is not possible by definition with
any file-based API.

> it creates a mapping that's not compatible with dynamic loading
> requirements *depending on* what the elf headers say.
> (elf segments may be loaded at various offsets that can conflict
> with the mapping that you just made)
No, conflict is not possible as they are
loaded to entirely separately mmapped buffer.
And I have a proof that this works as expected:

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


In this example you see 5 references to the
solib, all created by dlmem. There is no
conflict with the initial buffer, such conflict
is not even possible.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 15:12       ` stsp
@ 2023-03-31 17:12         ` Szabolcs Nagy
  2023-03-31 17:36           ` stsp
  2023-03-31 18:47           ` stsp
  0 siblings, 2 replies; 107+ messages in thread
From: Szabolcs Nagy @ 2023-03-31 17:12 UTC (permalink / raw)
  To: stsp, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

The 03/31/2023 20:12, stsp wrote:
> 31.03.2023 17:20, Szabolcs Nagy пишет:
> > The 03/29/2023 18:18, stsp via Libc-alpha wrote:
> > > Just to be more constructive, here is the
> > > example:
> > > 
> > > void *dlopen_with_offset(const char *file, off_t offset, int flags)
> > > {
> > this api requires no libc change:
> > 
> > dump the contents to a temp file and use dlopen on it.
> 
> This doesn't work for my use-case.
> dlmem() can optionally preserve the destination
> mapping, which is not possible by definition with
> any file-based API.

loading from special place and loading into special place
are different things.

you gave an example of the former but then talk about the
latter.

loading from special place can be solved by writing the
contents to a temp file and loading that.

loading into special place is difficult especially if you
want to control the details of segment mapping. in case of
your dlmem design it passes down a user callback that runs
under dynamic linker locks to map segments. the interface
contract of this callback is not documented, so it can
deadlock and exposes dynamic linker internals (makes
implementation changes harder later).

if all you want is force mapping below 4G then you should
have asked for an RTLD_ flag (which may not be useful
enough or may not be supportable on all relevant targets,
but at least does not need significant loader changes).

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 17:12         ` Szabolcs Nagy
@ 2023-03-31 17:36           ` stsp
  2023-04-01  9:28             ` stsp
  2023-04-03 10:04             ` Szabolcs Nagy
  2023-03-31 18:47           ` stsp
  1 sibling, 2 replies; 107+ messages in thread
From: stsp @ 2023-03-31 17:36 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Hi!

31.03.2023 22:12, Szabolcs Nagy пишет:
> loading from special place and loading into special place
> are different things.

Thank you for the technical comment!
Somehow that happened that this thread
went into non-technical very quickly. :(

> you gave an example of the former but then talk about the
> latter.

These are related.
File-based mmaps leave you no chance
of preserving the destination mapping,
so for my needs I took the interface for
which mmaps are not mandatory.
And then:
1. It suits my use-case surprisingly well
2. It suits as a base block for implementing
things like dlopen_with_offset(), which I
was planning to present in a subsequent
patch series. But given the fact that people
stopped believing my impl even works, I
am not so sure...

> loading from special place can be solved by writing the
> contents to a temp file and loading that.

That will end up with the casual mmaps(),
so loading to special place won't be achieved
as the result.

> loading into special place is difficult especially if you
> want to control the details of segment mapping.

Its difficult.
But my patch accumulated months of a work,
so its just here.


>   in case of
> your dlmem design it passes down a user callback that runs
> under dynamic linker locks to map segments. the interface
> contract of this callback is not documented, so it can
> deadlock

Good point.
But this is a documentation issue, right?
Not too much is needed: in the simple case
you only do anonymous mmap() in a callback.
In a more difficult case (my use-case) I do
shm_open(), ftruncate() and mmap().
Of course I need to document that any libdl
functions are disallowed, is this correct?


>   and exposes dynamic linker internals (makes
> implementation changes harder later).

Could you please clarify that part?
typedef void *
(dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
               void *cookie);

This only passes things needed for an initial
mmap(). The single mmap() that just defines
the mapping address.
What internals do you mean?

Also please note that the callback is optional
and is not needed for 99% of the interesting
cases possible with dlmem().

> if all you want is force mapping below 4G then you should
> have asked for an RTLD_ flag (which may not be useful
> enough or may not be supportable on all relevant targets,
> but at least does not need significant loader changes).
There is already such flag.
Unfortunately my setup is much more
complicated than that: I need to force
the mapping under 4Gb AND I need to
force it into the shared buffer which I
then mmap() to another address...
Yes, this may sound weird, and I am not
sure if you want to read my entire discussion
with Jonathon on why is that needed.
But he did a good small summary here:
https://sourceware.org/bugzilla/show_bug.cgi?id=30007#c8

Yes, its a technically difficult topic.
Yes, we spent months on only me explaining
my use-case! But why it suddenly downgraded
to flames, is a bit unclear.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 17:12         ` Szabolcs Nagy
  2023-03-31 17:36           ` stsp
@ 2023-03-31 18:47           ` stsp
  2023-03-31 19:00             ` stsp
  1 sibling, 1 reply; 107+ messages in thread
From: stsp @ 2023-03-31 18:47 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker


31.03.2023 22:12, Szabolcs Nagy пишет:
> your dlmem design it passes down a user callback that runs
> under dynamic linker locks to map segments.
Oh, I missed that sentence initially.
No, its not to map segments.
Its to do the single initial mmap() only.
The segments are of course all mapped
by the loader internally.
Which is why this callback is optional and
should almost never be used.

Does this clarification help?

Btw, I am thankful to you for reading an
API description. Not too many people did
that. Carlos and Jonathon definitely did,
so I am thankful to them as well.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 18:47           ` stsp
@ 2023-03-31 19:00             ` stsp
  0 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-03-31 19:00 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

[-- Attachment #1: Type: text/plain, Size: 1842 bytes --]

Szabolcs,

31.03.2023 23:47, stsp пишет:
>
> 31.03.2023 22:12, Szabolcs Nagy пишет:
>> your dlmem design it passes down a user callback that runs
>> under dynamic linker locks to map segments.
> Oh, I missed that sentence initially.
> No, its not to map segments.
> Its to do the single initial mmap() only.
> The segments are of course all mapped
> by the loader internally.
> Which is why this callback is optional and
> should almost never be used.
>
> Does this clarification help?
Actually let me show you the fdlopen()
example on top of dlmem, to make it
crystal clear that the premap callback
has nothing to do with anything, and
should just be ignored:

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

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

}

After running this code, we have this /proc/self/maps:

7fb413101000-7fb413102000 r--p 00000000 00:28 17195405 /home/stas/src/glibc-dev/build/dlfcn/glreflib1.so
7fb413102000-7fb413103000 r-xp 00001000 00:28 17195405 /home/stas/src/glibc-dev/build/dlfcn/glreflib1.so
7fb413103000-7fb413104000 r--p 00002000 00:28 17195405 /home/stas/src/glibc-dev/build/dlfcn/glreflib1.so
7fb413104000-7fb413105000 r--p 00002000 00:28 17195405 /home/stas/src/glibc-dev/build/dlfcn/glreflib1.so
7fb413105000-7fb413106000 rw-p 00003000 00:28 17195405 /home/stas/src/glibc-dev/build/dlfcn/glreflib1.so

So the callback is not involved.
As you can see its set to NULL.
The loader is fully capable, callback
is not needed.

Does this clarify?

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 17:36           ` stsp
@ 2023-04-01  9:28             ` stsp
  2023-04-03 10:04             ` Szabolcs Nagy
  1 sibling, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-01  9:28 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker


31.03.2023 22:36, stsp пишет:
> There is already such flag.

Namely, LD_PREFER_MAP_32BIT_EXEC.


> Unfortunately my setup is much more
> complicated than that: I need to force
> the mapping under 4Gb AND I need to
> force it into the shared buffer which I
> then mmap() to another address...
> Yes, this may sound weird, and I am not
> sure if you want to read my entire discussion
> with Jonathon on why is that needed.
> But he did a good small summary here:
> https://sourceware.org/bugzilla/show_bug.cgi?id=30007#c8
>
An additional clarification is needed that
the LD_AUDIT stuff mentioned in that URL,
was later replaced exactly by the premap
callback (that only optionally sets the
mapping area).

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 17:36           ` stsp
  2023-04-01  9:28             ` stsp
@ 2023-04-03 10:04             ` Szabolcs Nagy
  2023-04-03 10:43               ` stsp
  1 sibling, 1 reply; 107+ messages in thread
From: Szabolcs Nagy @ 2023-04-03 10:04 UTC (permalink / raw)
  To: stsp, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

The 03/31/2023 22:36, stsp wrote:
> 31.03.2023 22:12, Szabolcs Nagy пишет:
> > loading from special place and loading into special place
> > are different things.
> 
> These are related.
> File-based mmaps leave you no chance
> of preserving the destination mapping,

"preserving" the "destination mapping" (?)
a new requirement..

> > your dlmem design it passes down a user callback that runs
> > under dynamic linker locks to map segments. the interface
> > contract of this callback is not documented, so it can
> > deadlock
> 
> Good point.
> But this is a documentation issue, right?
> Not too much is needed: in the simple case
> you only do anonymous mmap() in a callback.
> In a more difficult case (my use-case) I do
> shm_open(), ftruncate() and mmap().
> Of course I need to document that any libdl
> functions are disallowed, is this correct?

that is not correct. (see below)

> >   and exposes dynamic linker internals (makes
> > implementation changes harder later).
> 
> Could you please clarify that part?
> typedef void *
> (dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
>               void *cookie);
> 
> This only passes things needed for an initial
> mmap(). The single mmap() that just defines
> the mapping address.
> What internals do you mean?

the segments of an elf file can be mmapped in many ways,
there is no such thing as initial mmap (there need not
be a mapping that covers all segments and there is no
ordering requirement for the mappings or which mapping
is file backed etc) your callback exposes a particular
behaviour.

and libc apis are exposed to not use locks that have
lock ordering requirement wrt the dynamic linker
internal lock. (e.g. we could add such a lock in mmap
or shm_open since they are not required to be as-safe.
so your callback can deadlock in a future glibc).

and it's not just locks but any unusual libc internal
state that becomes observable via the callback.
(the callback runs when the dynamic linker may be in
inconsistent internal state. why do you assume that
shm_open or other libc apis work in that case? this
is a new requirement that has to be documented.)

> Also please note that the callback is optional
> and is not needed for 99% of the interesting
> cases possible with dlmem().

it does not matter if it's 99% or 99.999%, what matters
is that if we expose an api then that has to work
*forever* under all potential usage and we have to
keep *supporting* it with all the api&abi constraints.

giving examples is not a *proof* that the api works.

> > if all you want is force mapping below 4G then you should
> > have asked for an RTLD_ flag (which may not be useful
> > enough or may not be supportable on all relevant targets,
> > but at least does not need significant loader changes).
> There is already such flag.

x86 specific flag.

> Unfortunately my setup is much more
> complicated than that: I need to force
> the mapping under 4Gb AND I need to
> force it into the shared buffer which I
> then mmap() to another address...

you didnt explain this in the rationale of the patches.

but it seems what you want is not suitable for libc.
you have to write your own loader to do this.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-03 10:04             ` Szabolcs Nagy
@ 2023-04-03 10:43               ` stsp
  2023-04-03 12:01                 ` Szabolcs Nagy
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-03 10:43 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Hi,

03.04.2023 15:04, Szabolcs Nagy пишет:
> "preserving" the "destination mapping" (?)
> a new requirement..

Indeed, which is the main point of that patch.
New requirement - new API.
Briefly, if the premap callback established some
MAP_SHARED mapping, then it will remain that
way after dlmem() finished working. But that's
an optional behavior, activated by a flag.
Normal tasks (like dlopen_with_offset()) need
nothing like that.
See the full details by an URL from my prev mail
about the use-case for such a thing.

> the segments of an elf file can be mmapped in many ways,
> there is no such thing as initial mmap (there need not
> be a mapping that covers all segments and there is no
> ordering requirement for the mappings or which mapping
> is file backed etc) your callback exposes a particular
> behaviour.

I think this is a misunderstanding.
Even before my patch there is this code in dl-load.c:

     /* Length of the sections to be loaded.  */
     maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart;

So it calculates the maximum length to map, which
is then mapped in 1 go in _dl_map_segment().
There is also this comment (again, w/o any patches
of mine):

          So we map the first segment without MAP_FIXED, but with its
          extent increased to cover all the segments.  Then we remove
          access from excess portion, and there is known sufficient space
          there to remap from the later segments.

So I am working on an existing frame-work only.

> and libc apis are exposed to not use locks that have
> lock ordering requirement wrt the dynamic linker
> internal lock. (e.g. we could add such a lock in mmap
> or shm_open since they are not required to be as-safe.
> so your callback can deadlock in a future glibc).

Well you probably can't add such a lock into mmap(),
as the loader calls mmap() anyway. Premap callback
just does the same thing.
If you can add some lock to shm_open(), then
shm_open() can simply be done before calling dlmem().
The only thing to care here, is probably ftruncate().
Do you really think some lock can be added to
ftruncate()?

> and it's not just locks but any unusual libc internal
> state that becomes observable via the callback.
> (the callback runs when the dynamic linker may be in
> inconsistent internal state. why do you assume that
> shm_open or other libc apis work in that case? this
> is a new requirement that has to be documented.)

Mainly mmap(), and the loader does mmap()s by
himself, so I quite assume they work from the
callback.

> it does not matter if it's 99% or 99.999%, what matters
> is that if we expose an api then that has to work
> *forever* under all potential usage and we have to
> keep *supporting* it with all the api&abi constraints.

Would it be acceptable to settle on an agreement
to not add a lock to ftruncate() that would prevent
its use from a premap callback? If its not possible,
of course there is still an option to document the
premap callback in a way so it will have to do the
syscalls directly. Which doesn't look like a very
good choice, but possible.


> giving examples is not a *proof* that the api works.

The example was needed to demonstrate that
premap is not needed to map segments.


>> Unfortunately my setup is much more
>> complicated than that: I need to force
>> the mapping under 4Gb AND I need to
>> force it into the shared buffer which I
>> then mmap() to another address...
> you didnt explain this in the rationale of the patches.

I mentioned such a use-case actually in the
rationale, here's the quote:

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.


Should I make is more verbose?

> but it seems what you want is not suitable for libc.
> you have to write your own loader to do this.
I very much wish to do that!
But so far I failed with a few naive attempts,
like building the patched glibc statically, or
loading custom libc.so into a separate name-space...
I don't know what interface should be exposed
by glibc to let my own loader to create a new
link-map and bind symbols to it. Maybe even
none, maybe I can do that via r_debug for example.
So I very much wish to work in that direction, if you
think it is a more appropriate solution that
can be accepted into glibc (if any changes are
at all needed).

However, dlmem() can be used for Android's
dlopen_with_offset(), which was also rejected
from glibc. With dlmem() its just a dozen of
lines that never need to be in glibc itself. So
for example the problem with dlopen_with_offset()
could be easily settled with my dlmem().


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-03 10:43               ` stsp
@ 2023-04-03 12:01                 ` Szabolcs Nagy
  2023-04-03 13:07                   ` stsp
  2023-04-05  7:29                   ` stsp
  0 siblings, 2 replies; 107+ messages in thread
From: Szabolcs Nagy @ 2023-04-03 12:01 UTC (permalink / raw)
  To: stsp, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

The 04/03/2023 15:43, stsp via Libc-alpha wrote:
> 03.04.2023 15:04, Szabolcs Nagy пишет:
> > the segments of an elf file can be mmapped in many ways,
> > there is no such thing as initial mmap (there need not
> > be a mapping that covers all segments and there is no
> > ordering requirement for the mappings or which mapping
> > is file backed etc) your callback exposes a particular
> > behaviour.
> 
> I think this is a misunderstanding.
> Even before my patch there is this code in dl-load.c:

these are all *internals*, there is nothing in the
public api (or even elf spec) that says this is how
this should be done.

> So I am working on an existing frame-work only.

no, you are exposing implementation internal details.

> > and libc apis are exposed to not use locks that have
> > lock ordering requirement wrt the dynamic linker
> > internal lock. (e.g. we could add such a lock in mmap
> > or shm_open since they are not required to be as-safe.
> > so your callback can deadlock in a future glibc).
> 
> Well you probably can't add such a lock into mmap(),
> as the loader calls mmap() anyway. Premap callback
> just does the same thing.

no, the loader does not call the public mmap libc api.
it calls internal __mmap that may behave differently.

the current implementation does not impose any
requirement on the public mmap symbol.

> Do you really think some lock can be added to
> ftruncate()?

we have to reason about the constraints not second
guess what others think, we have plenty syscalls
emulated in the libc for posix conformance reasons
doing non-trivial things and various libc apis are
interposed by user code (see asan) and then they can
do whatever (e.g. asan interposes mmap, but cannot
interpose the internal __mmap).

> > and it's not just locks but any unusual libc internal
> > state that becomes observable via the callback.
> > (the callback runs when the dynamic linker may be in
> > inconsistent internal state. why do you assume that
> > shm_open or other libc apis work in that case? this
> > is a new requirement that has to be documented.)
> 
> Mainly mmap(), and the loader does mmap()s by
> himself, so I quite assume they work from the
> callback.

again, this is wrong.

> > it does not matter if it's 99% or 99.999%, what matters
> > is that if we expose an api then that has to work
> > *forever* under all potential usage and we have to
> > keep *supporting* it with all the api&abi constraints.
> 
> Would it be acceptable to settle on an agreement
> to not add a lock to ftruncate() that would prevent
> its use from a premap callback? If its not possible,
> of course there is still an option to document the
> premap callback in a way so it will have to do the
> syscalls directly. Which doesn't look like a very
> good choice, but possible.

a user callback must not run in a context where libc
internals are exposed (e.g. under internal locks).
if you do that you need very strong justification
and very careful specification of the api. (moving
the callback outside the dynlinker locks or passing
down flags instead of a callback would solve this
particular issue.)

> > > Unfortunately my setup is much more
> > > complicated than that: I need to force
> > > the mapping under 4Gb AND I need to
> > > force it into the shared buffer which I
> > > then mmap() to another address...
> > you didnt explain this in the rationale of the patches.
> 
> I mentioned such a use-case actually in the
> rationale, here's the quote:
> 
> 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.

there are so many unexplained details here.. we have
to guess what you have in mind. (should tls also end
up in shared memory? what if the libc wants to generate
executable code per module for instrumentation? e.g.
for PLT hooking, should that go to the shared mapping
too? what if the malloc implementation reuses bits of
memory from shared libs they waste due to page alignment
is that legal? we can answer these questions if you
either specify the interface contracts or the point of
all of this, but right now it's anybody's guess.)

libc will likely not include an api to load libs into
shared memory. (e.g. aarch64 has features that work on
anon or file backed memory but not on shared memory and
some systems may prevent it for security reasons, so
this has at least portability problems, but it also
clearly constrains the implementation.)

> > but it seems what you want is not suitable for libc.
> > you have to write your own loader to do this.
> I very much wish to do that!
> But so far I failed with a few naive attempts,
> like building the patched glibc statically, or
> loading custom libc.so into a separate name-space...

you can load a shared lib / generate code that does
not use normal libc shared library abi, but can
call into libc (or some other library) or can be
called from normal libraries. this is kind of what
jits do.

> I don't know what interface should be exposed
> by glibc to let my own loader to create a new
> link-map and bind symbols to it. Maybe even
> none, maybe I can do that via r_debug for example.
> So I very much wish to work in that direction, if you
> think it is a more appropriate solution that
> can be accepted into glibc (if any changes are
> at all needed).
> 
> However, dlmem() can be used for Android's
> dlopen_with_offset(), which was also rejected
> from glibc. With dlmem() its just a dozen of
> lines that never need to be in glibc itself. So
> for example the problem with dlopen_with_offset()
> could be easily settled with my dlmem().

there is probably a reason why that api got rejected
and likely the same reason applies to dlmem.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-03 12:01                 ` Szabolcs Nagy
@ 2023-04-03 13:07                   ` stsp
  2023-04-05  7:29                   ` stsp
  1 sibling, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-03 13:07 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Hello,

03.04.2023 17:01, Szabolcs Nagy пишет:
> these are all *internals*, there is nothing in the
> public api (or even elf spec) that says this is how
> this should be done.

Yes, OTOH of course its reasonable to expect
that the solib's mapping size's upper limit can
be estimated before doing the actual mapping.
So I rely on such estimation code which is already
there.

Maybe it would be a solution to add another
function that will return such a size estimation?
Well, I am sure this also have dozens of down-sides...

>>> and libc apis are exposed to not use locks that have
>>> lock ordering requirement wrt the dynamic linker
>>> internal lock. (e.g. we could add such a lock in mmap
>>> or shm_open since they are not required to be as-safe.
>>> so your callback can deadlock in a future glibc).
>> Well you probably can't add such a lock into mmap(),
>> as the loader calls mmap() anyway. Premap callback
>> just does the same thing.
> no, the loader does not call the public mmap libc api.
> it calls internal __mmap that may behave differently.
>
> the current implementation does not impose any
> requirement on the public mmap symbol.

An option may be to pass a premap callback
with a pointers to such __mmap, __ftruncate. But
of course not the best option again. :(

> a user callback must not run in a context where libc
> internals are exposed (e.g. under internal locks).
> if you do that you need very strong justification
> and very careful specification of the api. (moving
> the callback outside the dynlinker locks or passing
> down flags instead of a callback would solve this
> particular issue.)

I considered passing flags and fd.
This makes things much less flexible, but
the real problem here is to set the relocation
address. premap callback just returns it
after doing the mapping of a requested size.
Without knowing the estimated size beforehand,
you unfortunately can't easily go that route. :(
The func to get the estimated size, could help.


> there are so many unexplained details here.. we have
> to guess what you have in mind. (should tls also end
> up in shared memory? what if the libc wants to generate
> executable code per module for instrumentation? e.g.
> for PLT hooking, should that go to the shared mapping
> too? what if the malloc implementation reuses bits of
> memory from shared libs they waste due to page alignment
> is that legal? we can answer these questions if you
> either specify the interface contracts or the point of
> all of this, but right now it's anybody's guess.)

No, no, its much simpler! :)
I just put _entire_ solib into a shared mapping.
I do not care about tls or anything.
All I do is create the initial large mapping, that
remains unaffected by the loader. Even the
inter-segment gaps you mentioned, or the
anonymously mapped parts like .bss - just all
go into that initial shared mapping. Which is
why I haven't described such details. That are
simply not important in that case.

> libc will likely not include an api to load libs into
> shared memory. (e.g. aarch64 has features that work on
> anon or file backed memory but not on shared memory and
> some systems may prevent it for security reasons, so
> this has at least portability problems, but it also
> clearly constrains the implementation.)

Well, from the API stand-point its not mapping
into a shared memory. Its mapping into where's
the premap callback decided. Can we assume
that the premap callback is responsible to not
do stupid things on aarch64, where mapping
to shared memory will not work as expected?


> you can load a shared lib / generate code that does
> not use normal libc shared library abi, but can
> call into libc (or some other library) or can be
> called from normal libraries. this is kind of what
> jits do.

I wanted to load a solib and get its DT_NEEDED
deps all resolved. If I had some way of linking
things with my patched, statically built libc, that
would work. Maybe I can rip off the glibc loader
and somehow build it separately, etc. So what
you say, may work, but seems like a tremendous
task of either writing a full loader or ripping off
the one from glibc...
And even that route will only allow me to use
such libs in a separate NS, as in base NS it
won't have a link-map visible by glibc. But
having that in a separate NS is acceptable for
my needs, so eventually I may go that route
if all else fails.


> there is probably a reason why that api got rejected
> and likely the same reason applies to dlmem.
Likely, but I just thought having an android
extensions in, is not so bad for glibc (of course
its not me to decide). So having a building block
that supports dlopen_with_offset() being externally
implemented, looked like a tasty thing for glibc,
but perhaps it wasn't the case.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-03 12:01                 ` Szabolcs Nagy
  2023-04-03 13:07                   ` stsp
@ 2023-04-05  7:29                   ` stsp
  2023-04-05  8:51                     ` Szabolcs Nagy
  1 sibling, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-05  7:29 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Hi Szabolcs,

03.04.2023 17:01, Szabolcs Nagy пишет:
> a user callback must not run in a context where libc
> internals are exposed (e.g. under internal locks).
I checked that part a bit more, and so far
my findings are (let me know if they are wrong):
- That code indeed works under dl_load_lock,
   but it calls audit callbacks rather freely, and
   I don't see it releasing the lock before calling
   the user audit callbacks.

- dl_iterate_phdr() seems to be calling the user
   callback under dl_load_write_lock lock.

So my callback is called in a same context
where audit callbacks are called, and also
other callbacks seem to have no problem
being called under various locks.

So in that sense why my callback is different,
or what am I missing?


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-05  7:29                   ` stsp
@ 2023-04-05  8:51                     ` Szabolcs Nagy
  2023-04-05  9:26                       ` stsp
                                         ` (2 more replies)
  0 siblings, 3 replies; 107+ messages in thread
From: Szabolcs Nagy @ 2023-04-05  8:51 UTC (permalink / raw)
  To: stsp, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

The 04/05/2023 12:29, stsp wrote:
> Hi Szabolcs,
> 
> 03.04.2023 17:01, Szabolcs Nagy пишет:
> > a user callback must not run in a context where libc
> > internals are exposed (e.g. under internal locks).
> I checked that part a bit more, and so far
> my findings are (let me know if they are wrong):
> - That code indeed works under dl_load_lock,
>   but it calls audit callbacks rather freely, and
>   I don't see it releasing the lock before calling
>   the user audit callbacks.
> 
> - dl_iterate_phdr() seems to be calling the user
>   callback under dl_load_write_lock lock.

this is a known bug.

(audit is special because it is not an api for applications
but for tooling that hooks into dynamic linker internals,
it has its own abi versioning so we can break the abi more
easily and those tools are expected to cope with it. it is
necessarily exposing internals, so new hooks are more likely
acceptable there, but that's not true for apis designed for
application use.)

a much bigger issue is calling user ctors under the
dynamic linker lock which did cause issues in practice.

another issue is calling malloc under locks when malloc
can be interposed (likewise it caused issues in practice).

i did not say glibc was clean from these problems, but
that does not mean we want to add more to it.

> 
> So my callback is called in a same context
> where audit callbacks are called, and also
> other callbacks seem to have no problem
> being called under various locks.

they have problems.

in any case dlmem has way too many issues beyond
the locking one, the fact that you don't document
nor understand the issues around the huge interface
boundary it creates does not help either.

to successfully propose a new api you need to use
the same rigor as posix or c standardization would.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-05  8:51                     ` Szabolcs Nagy
@ 2023-04-05  9:26                       ` stsp
  2023-04-05  9:31                       ` Florian Weimer
  2023-04-12 17:23                       ` stsp
  2 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-05  9:26 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker


05.04.2023 13:51, Szabolcs Nagy пишет:
> The 04/05/2023 12:29, stsp wrote:
>> Hi Szabolcs,
>>
>> 03.04.2023 17:01, Szabolcs Nagy пишет:
>>> a user callback must not run in a context where libc
>>> internals are exposed (e.g. under internal locks).
>> I checked that part a bit more, and so far
>> my findings are (let me know if they are wrong):
>> - That code indeed works under dl_load_lock,
>>    but it calls audit callbacks rather freely, and
>>    I don't see it releasing the lock before calling
>>    the user audit callbacks.
>>
>> - dl_iterate_phdr() seems to be calling the user
>>    callback under dl_load_write_lock lock.
> this is a known bug.
>
> (audit is special because it is not an api for applications
> but for tooling that hooks into dynamic linker internals,
I had the version of the patch that
added an audit callback instead of
the direct callback.
Do you think that's going to be better?
In that case the dlmem() API is only
documented for the basic tasks of loading
a solib from memory.
Audit module can then add an advanced
functionality, but its not going to be a
part of glibc.
The problems around documenting maplength
are not available in such an impl, since
that became internal to an audit callback.


> in any case dlmem has way too many issues beyond
> the locking one, the fact that you don't document
> nor understand the issues around the huge interface
> boundary it creates does not help either.
I understand the problem around maplength,
and I wonder if getting back to an audit
callback may be a solution to this and all
the other problems you mentioned.
Essentially in this case glibc has only a very
small and simple call, and the rest is implemented
by an author of an audit module.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-05  8:51                     ` Szabolcs Nagy
  2023-04-05  9:26                       ` stsp
@ 2023-04-05  9:31                       ` Florian Weimer
  2023-04-12 17:23                       ` stsp
  2 siblings, 0 replies; 107+ messages in thread
From: Florian Weimer @ 2023-04-05  9:31 UTC (permalink / raw)
  To: Szabolcs Nagy via Libc-alpha
  Cc: stsp, Adhemerval Zanella Netto, janderson, Carlos O'Donell,
	Rich Felker, Szabolcs Nagy

* Szabolcs Nagy via Libc-alpha:

> The 04/05/2023 12:29, stsp wrote:
>> - dl_iterate_phdr() seems to be calling the user
>>   callback under dl_load_write_lock lock.
>
> this is a known bug.

It's also not something we can fix because the libgcc unwinder has code
on it that relies on this implicit loader lock to protect its internal
data structures.  The libgcc unwinder can be statically linked, so we
can't remove the locking without adding a new symbol version.

I suspect other uses of dl_iterate_phdr are similar.

Thanks,
Florian


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-05  8:51                     ` Szabolcs Nagy
  2023-04-05  9:26                       ` stsp
  2023-04-05  9:31                       ` Florian Weimer
@ 2023-04-12 17:23                       ` stsp
  2023-04-12 18:00                         ` stsp
  2 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-12 17:23 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker

Hello,

05.04.2023 13:51, Szabolcs Nagy пишет:
> this is a known bug.
>
> (audit is special because it is not an api for applications
> but for tooling that hooks into dynamic linker internals,
> it has its own abi versioning so we can break the abi more
> easily and those tools are expected to cope with it. it is
> necessarily exposing internals, so new hooks are more likely
> acceptable there, but that's not true for apis designed for
> application use.)
>
> a much bigger issue is calling user ctors under the
> dynamic linker lock which did cause issues in practice.
>
> another issue is calling malloc under locks when malloc
> can be interposed (likewise it caused issues in practice).
>
> i did not say glibc was clean from these problems, but
> that does not mean we want to add more to it.
OK, in an attempt to address these concerns
I am currently trying to implement an entirely
different approach.
The basic idea is that dlmem() can leave an
object unrelocated. This is a small change,
relocation is currently done in dl_open_worker_begin().
But I can do it upon a subsequent call of an
any dlfcn function with the handle of that object,
by just checking the l->relocated flag that is
already present in the link_map struct.
The same "lazy relocation" can even be
applied to a regular dlopen().

Now the trick is that when you have an
unrelocated object, you can move it by
hands to the desired address (preserving
the page protection, that's easy) and call
dlsetbase(handle, address);
to update a couple of link-map pointers.
dlsetbase() would fail if the object is
already relocated, so it should be called
right after dlmem() or dlopen(), or not called
at all if you don't need to alter the solib
location.
To get a mapaddr and maplength of a loaded
solib I am going to add a trivial getters for the
needed values from a link-map. These are
only needed if one wants to move the loaded
unrelocated solib.

That still fits my use-case very well, doesn't
require a callback and doesn't require the user
to know the maplength beforehand.
So it would seem to address all of your concerns,
and overall seems a much simpler approach.
But it would require an additional dlsetaddr()
trivial function and a few link-map getters,
besides dlmem() itself.

Would this approach be viable?

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-12 17:23                       ` stsp
@ 2023-04-12 18:00                         ` stsp
  2023-04-12 18:20                           ` Rich Felker
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-12 18:00 UTC (permalink / raw)
  To: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell, Rich Felker


12.04.2023 22:23, stsp пишет:
> The same "lazy relocation" can even be
> applied to a regular dlopen().
Of course it can't, as dlopen() is expected
to call library ctors. So only with an optional
dlmem() flag such behavior should be performed,
in which case dlsetbase() should also do the
final relocation and ctors calling.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-12 18:00                         ` stsp
@ 2023-04-12 18:20                           ` Rich Felker
  2023-04-12 18:46                             ` stsp
                                               ` (2 more replies)
  0 siblings, 3 replies; 107+ messages in thread
From: Rich Felker @ 2023-04-12 18:20 UTC (permalink / raw)
  To: stsp
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell

On Wed, Apr 12, 2023 at 11:00:19PM +0500, stsp wrote:
> 
> 12.04.2023 22:23, stsp пишет:
> >The same "lazy relocation" can even be
> >applied to a regular dlopen().
> Of course it can't, as dlopen() is expected
> to call library ctors. So only with an optional
> dlmem() flag such behavior should be performed,
> in which case dlsetbase() should also do the
> final relocation and ctors calling.

Have you not realized yet how far outside any concept of "reasonable"
this ever-expanding contract is?

Whatever happens, this whole proposal is going to be rejected, over
and over, and the more effort you pour into engineering it on the
belief that it will somehow eventually be accepted, the more
disappointed (and maybe angry?) you're going to be at the people who
have to keep rejecting it. For the sake of your time and that of the
folks who have been pulled into this discussion, I think it would
really, *really* be a good idea to go figure out how to solve your
practical problem without trying to put new functionality in libc.

Rich

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-12 18:20                           ` Rich Felker
@ 2023-04-12 18:46                             ` stsp
  2023-04-12 19:52                               ` Zack Weinberg
  2023-04-12 19:07                             ` stsp
  2023-04-13 10:01                             ` stsp
  2 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-12 18:46 UTC (permalink / raw)
  To: Rich Felker
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell


12.04.2023 23:20, Rich Felker пишет:
> On Wed, Apr 12, 2023 at 11:00:19PM +0500, stsp wrote:
>> 12.04.2023 22:23, stsp пишет:
>>> The same "lazy relocation" can even be
>>> applied to a regular dlopen().
>> Of course it can't, as dlopen() is expected
>> to call library ctors. So only with an optional
>> dlmem() flag such behavior should be performed,
>> in which case dlsetbase() should also do the
>> final relocation and ctors calling.
> Have you not realized yet how far outside any concept of "reasonable"
> this ever-expanding contract is?

How can I realize that, if it was never
explained? In fact, the only API review
was from Szabolcs, and he just pointed
to 2 problems that are seemingly solvable.

> Whatever happens, this whole proposal is going to be rejected, over
> and over, and the more effort you pour into engineering it on the
> belief that it will somehow eventually be accepted, the more
> disappointed (and maybe angry?) you're going to be at the people who
> have to keep rejecting it.

If no one spells out the proper reason,
then maybe.
Is it that difficult to spell out the reason
of a rejection?


>   For the sake of your time and that of the
> folks who have been pulled into this discussion, I think it would
> really, *really* be a good idea to go figure out how to solve your
> practical problem without trying to put new functionality in libc.
In fact, instead of spelling out the rejection
reason, all discussions were revolving around
solving my problem w/o modifying libc.
Basically there are currently 2 variations of
such solutions:
1. Intercept glibc's loader mmap()s on a syscall
level and bind a non-trivial logic to them (Jonathon's
proposal)
2. Implement the dynamic loader outside of glibc
(Szabolcs's proposal, and also my own wish).

I'd be happy to implement 2, but glibc doesn't
have the hooks needed for external dynamic
loader, and it doesn't even allow to use the
statically-linked patched glibc as such a loader
in a separate namespace.
Which is why so far I haven't seen a viable
proposal on solving my problem, and haven't
seen a clear explanation why the change to
glibc is going to be rejected even if the raised
concerns are all addressed.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-12 18:20                           ` Rich Felker
  2023-04-12 18:46                             ` stsp
@ 2023-04-12 19:07                             ` stsp
  2023-04-13 10:01                             ` stsp
  2 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-12 19:07 UTC (permalink / raw)
  To: Rich Felker
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell


12.04.2023 23:20, Rich Felker пишет:
> On Wed, Apr 12, 2023 at 11:00:19PM +0500, stsp wrote:
>> 12.04.2023 22:23, stsp пишет:
>>> The same "lazy relocation" can even be
>>> applied to a regular dlopen().
>> Of course it can't, as dlopen() is expected
>> to call library ctors. So only with an optional
>> dlmem() flag such behavior should be performed,
>> in which case dlsetbase() should also do the
>> final relocation and ctors calling.
> Have you not realized yet how far outside any concept of "reasonable"
> this ever-expanding contract is?
Would it be more consistent to always
require a pair of dlmem()/dlinitialize(handle, addr) calls?
Or the 2-step loading is out of any considerations?

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-12 18:46                             ` stsp
@ 2023-04-12 19:52                               ` Zack Weinberg
  0 siblings, 0 replies; 107+ messages in thread
From: Zack Weinberg @ 2023-04-12 19:52 UTC (permalink / raw)
  To: GNU libc development

On Wed, Apr 12, 2023, at 2:46 PM, stsp via Libc-alpha wrote:
> 12.04.2023 23:20, Rich Felker пишет:
>> On Wed, Apr 12, 2023 at 11:00:19PM +0500, stsp wrote:
>>> 12.04.2023 22:23, stsp пишет:
>>>> The same "lazy relocation" can even be applied to a regular
>>>> dlopen().
>>> Of course it can't, as dlopen() is expected to call library ctors.
>>> So only with an optional dlmem() flag such behavior should be
>>> performed, in which case dlsetbase() should also do the final
>>> relocation and ctors calling.
>> Have you not realized yet how far outside any concept of "reasonable"
>> this ever-expanding contract is?
>
> How can I realize that, if it was never explained? In fact, the only
> API review was from Szabolcs, and he just pointed to 2 problems that
> are seemingly solvable.

This discussion keeps going in circles.  You, Stas, think you aren't
getting any explanation for why your API proposals are being rejected.
Meanwhile, Szabolcs, Adhemerval, Carlos, Rich, etc. all think they ARE
giving you explanations for why they don't like this API.

Let me try to highlight specific issues from the feedback you've been
getting.  It appears to me that your *API* proposal -- not any details
of its implementation, the API itself -- is unacceptable *BECAUSE*:

* It introduces a callback from the dynamic loader into user code, that
  is to be run with an internal lock held. glibc does already have
  callbacks like that, but we do not want to introduce any more of
  them, period.
* It assumes that the dynamic loader will always, in the future, operate
  by creating a large initial mapping and then modifying protection
  flags on it.
* It assumes that it the dynamic loader is, and will always be, capable
  of taking a pointer to a block of memory -- no matter how it was
  originally allocated -- and then chopping its virtual memory map up to
  satisfy the requirements of the embedded ELF program header table,
  without breaking anything else.  [On this specific note, I foresee
  someone trying to write a dynamic module into memory allocated with
  memalign() or even plain old malloc(); calling dlmem() on this is very
  likely to apply PROT_NONE to address ranges containing malloc
  metadata!]

Carlos also gave you the specific feedback that he thinks an API
structured around handing the dynamic loader a file descriptor, not a
pointer, would be much more likely to be accepted.  Your response to
this was "dlmem() can optionally preserve the destination mapping, which
is not possible by definition with any file-based API."  I accept that
_you_ think that, but I don't think it's true, myself.  If your goal is
to force a shared library to be loaded into memory with specific
characteristics, I can think of at least two designs that ought to be
able to achieve that goal while still starting from the same place as
fdlopen().  For instance, maybe we could have an fdlopen variant that
called callback functions instead of mmap() and mprotect().  Unlike
your dlmem_premap_t, I think it ought to be possible to avoid doing
those specific operations with any locks held.

zw

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-12 18:20                           ` Rich Felker
  2023-04-12 18:46                             ` stsp
  2023-04-12 19:07                             ` stsp
@ 2023-04-13 10:01                             ` stsp
  2023-04-13 12:38                               ` Szabolcs Nagy
  2 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-13 10:01 UTC (permalink / raw)
  To: Rich Felker
  Cc: Szabolcs Nagy, Adhemerval Zanella Netto, libc-alpha, janderson,
	Carlos O'Donell


12.04.2023 23:20, Rich Felker пишет:
> On Wed, Apr 12, 2023 at 11:00:19PM +0500, stsp wrote:
>> 12.04.2023 22:23, stsp пишет:
>>> The same "lazy relocation" can even be
>>> applied to a regular dlopen().
>> Of course it can't, as dlopen() is expected
>> to call library ctors. So only with an optional
>> dlmem() flag such behavior should be performed,
>> in which case dlsetbase() should also do the
>> final relocation and ctors calling.
> Have you not realized yet how far outside any concept of "reasonable"
> this ever-expanding contract is?

OK, consider this small proposal (not dlmem):

RTLD_NORELOC - new flag to skip the relocation step.

dlrelocate(handle, base) - new call to explicitly
perform the relocation (and call ctors) that was
skipped with RTLD_NORELOC flag.

That alone already solves most of what I wanted
to achieve with dlmem(). Namely, it allows the
user to specify the relocation address he needs.
He does so by manually moving the solib image
to the desired location, and then calling dlrelocate()
with new base address.

Could you please tell me beforehand why that
proposal will be rejected?


> Whatever happens, this whole proposal is going to be rejected, over
> and over,

I believe you, but the question is still "why".
In all the other projects I contributed in, the
proposals were in most cases getting the
change requests. And in glibc, the only available
change request is to drop the proposal? :)


>   and the more effort you pour into engineering it on the
> belief that it will somehow eventually be accepted, the more
> disappointed (and maybe angry?) you're going to be at the people who
> have to keep rejecting it.
This is a wrong assumption, because in a
worst case this code will make it into a standalone
loader. So far I know how to do that only until
glibc changes one of its core structs, like rtld_global_ro
or link_map. With every such change, the loader
will stop working. So that would be a very unfortunate
and unreliable solution. I'd like to avoid it, but
there is no risk that my efforts working on that
code will be wasted.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 10:01                             ` stsp
@ 2023-04-13 12:38                               ` Szabolcs Nagy
  2023-04-13 15:59                                 ` stsp
  2023-05-08 15:10                                 ` [PATCH v9 0/13] implement dlmem() function stsp
  0 siblings, 2 replies; 107+ messages in thread
From: Szabolcs Nagy @ 2023-04-13 12:38 UTC (permalink / raw)
  To: stsp, Rich Felker
  Cc: Adhemerval Zanella Netto, libc-alpha, janderson, Carlos O'Donell

The 04/13/2023 15:01, stsp wrote:
> 12.04.2023 23:20, Rich Felker пишет:
> > On Wed, Apr 12, 2023 at 11:00:19PM +0500, stsp wrote:
> > > 12.04.2023 22:23, stsp пишет:
> > > > The same "lazy relocation" can even be
> > > > applied to a regular dlopen().
> > > Of course it can't, as dlopen() is expected
> > > to call library ctors. So only with an optional
> > > dlmem() flag such behavior should be performed,
> > > in which case dlsetbase() should also do the
> > > final relocation and ctors calling.
> > Have you not realized yet how far outside any concept of "reasonable"
> > this ever-expanding contract is?
> 
> OK, consider this small proposal (not dlmem):
> 
> RTLD_NORELOC - new flag to skip the relocation step.
> 
> dlrelocate(handle, base) - new call to explicitly
> perform the relocation (and call ctors) that was
> skipped with RTLD_NORELOC flag.
> 
> That alone already solves most of what I wanted
> to achieve with dlmem(). Namely, it allows the
> user to specify the relocation address he needs.
> He does so by manually moving the solib image
> to the desired location, and then calling dlrelocate()
> with new base address.
> 
> Could you please tell me beforehand why that
> proposal will be rejected?

it does not allow handling relocation failures sensibly,
does not specify what happens with dependencies and
exposes half loaded modules to users with unclear use-case
or semantics for them.

> > Whatever happens, this whole proposal is going to be rejected, over
> > and over,
> 
> I believe you, but the question is still "why".
> In all the other projects I contributed in, the
> proposals were in most cases getting the
> change requests. And in glibc, the only available
> change request is to drop the proposal? :)

if you fix an actual bug that's accepted.

you are adding a new interface without gathering consensus
that it's a good idea at all or how it should work.

> >   and the more effort you pour into engineering it on the
> > belief that it will somehow eventually be accepted, the more
> > disappointed (and maybe angry?) you're going to be at the people who
> > have to keep rejecting it.
> This is a wrong assumption, because in a
> worst case this code will make it into a standalone
> loader. So far I know how to do that only until
> glibc changes one of its core structs, like rtld_global_ro
> or link_map. With every such change, the loader
> will stop working. So that would be a very unfortunate
> and unreliable solution. I'd like to avoid it, but
> there is no risk that my efforts working on that
> code will be wasted.

when i said you should write your own loader i expected
you to create simple stubs in memory at the location
you want them to be and bind them to a library that
is loaded by libc (so you can connect your weird world
with the libc world).

it looked like a simple solution to me, but i still dont
know exactly your requirements so in a sense we still
don't have a well understood use-case for any of your
proposed changes. so maye the use-case is where you
should start.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 12:38                               ` Szabolcs Nagy
@ 2023-04-13 15:59                                 ` stsp
  2023-04-13 18:09                                   ` Adhemerval Zanella Netto
  2023-05-08 15:10                                 ` [PATCH v9 0/13] implement dlmem() function stsp
  1 sibling, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-13 15:59 UTC (permalink / raw)
  To: Szabolcs Nagy, Rich Felker
  Cc: Adhemerval Zanella Netto, libc-alpha, janderson, Carlos O'Donell

Hi,

13.04.2023 17:38, Szabolcs Nagy пишет:
> it does not allow handling relocation failures sensibly,
> does not specify what happens with dependencies and
> exposes half loaded modules to users with unclear use-case
> or semantics for them.

Thanks, lets update the spec to address this:

RTLD_NORELOC - do not relocate the object, allowing
to perform the relocation later. Ctors are delayed, and
are called immediately after the relocation is done.
Relocation is performed upon the first operation on
the returned handle, before the requested operation
on a handle is performed, with the exception of
dlset_object_baseaddr() which doesn't perform the
relocation. If this flag is used, it is recommended to
use dlrelocate() to explicitly perform the relocation
and check the result of it. This flag doesn't delay
the load of an object deps and the calls to their ctors.
It also doesn't delay the LA_ACT_CONSISTENT
audit event.

dlset_object_baseaddr(handle, addr) - sets the base
address of an unrelocated object. Returns error if
the object is already relocated. The base address
set by this function, will be used when relocation
is performed.

dlrelocate(handle) - perform the object relocation
if the RTLD_NORELOC flag was used, return EINVAL
if already relocated. This function may be omitted
even if RTLD_NORELOC was used, in which case
the relocation will be performed upon the first
operation on a handle, but using that function
allows to handle relocation errors and run ctors
before using the object's handle. If the function
returned success then ctors of an object were
called by it. If it returned error other than EINVAL
(EINVAL means object already relocated), then
relocation error happened and the handle should
be closed with dlclose().


It seems that spec covers all of your concerns.
It does cover even the exposure of "half-loaded
modules" by stating that the relocation may
happen lazily so things would work as expected
in any case.

> if you fix an actual bug that's accepted.

I believe this classifies as a fix:
https://patchwork.ozlabs.org/project/glibc/patch/20230301150818.31815-2-stsp2@yandex.ru/

> you are adding a new interface without gathering consensus
> that it's a good idea at all or how it should work.

I can get such a consensus only if people,
like you do (many thanks for that!), explain
the problems to me and suggest an amendments.
Unfortunately, its not the case. People are
saying that my proposal is bad, and the only
suggested amendment is to drop it immediately. :)

But I am very thankful to those who at least did
some side-activities for me, like discussing my
use-case and reviewing part of the patches.
These activities were very valuable, but the real
problems were never discussed before, proposed
API was not reviewed, and so on.

And with your input I already advanced to the
point where the API can be split into 2 parts,
whereas before I thought of dlmem() as a
thing in itself that can't be split. Now I see
that RTLD_NORELOC can solve the biggest part
of the puzzle alone.


> when i said you should write your own loader i expected
> you to create simple stubs in memory at the location
> you want them to be and bind them to a library that
> is loaded by libc (so you can connect your weird world
> with the libc world).
>
> it looked like a simple solution to me, but i still dont
> know exactly your requirements so in a sense we still
> don't have a well understood use-case for any of your
> proposed changes. so maye the use-case is where you
> should start.
You mean "restart". :)
As I already explained it very thoroughly in the
bugzilla, but you missed that and now its better
to repeat, since the bugzilla entry had a very
lengthy discussions afterwards.

The use-case is a VM with 32bit code inside.
It has a 4Gb window mapped somewhere in
a 64bit space (call it VM_window_start).
The task is very simple: first I find the suitable
mapping address within a VM address space.
Such address is within the 0-4Gb boundaries.
I relocate the solib to that "low" address.
Unfortunately this is still not visible to the VM,
because VM's window is at VM_window_start,
as we remember. So in order for VM-running
32bit code to access the solib's data (like .bss,
.rodata) when it gets a 32bit pointer to such
data, I also need to map the full duplicate of
the solib object into the address that looks like:

dupe_addr = VM_window_start + reloc_addr

For that I invented the optional dlmem flag
that doesn't destroy the destination mapping.
But such flag doesn't survive your requirement
to get rid of a call-back, so in the next prop
I'll pass a shm fd to dlmem instead, and an
optional flag will ask dlmem() to mmap that
fd as a destination backing-store.

So I am sure all problems can be solved!
But of course I can't do that alone, if everyone
just keeps saying "drop your patches immediately".

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 15:59                                 ` stsp
@ 2023-04-13 18:09                                   ` Adhemerval Zanella Netto
  2023-04-13 18:59                                     ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-04-13 18:09 UTC (permalink / raw)
  To: stsp, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell



On 13/04/23 12:59, stsp wrote:
> 
>> you are adding a new interface without gathering consensus
>> that it's a good idea at all or how it should work.
> 
> I can get such a consensus only if people,
> like you do (many thanks for that!), explain
> the problems to me and suggest an amendments.
> Unfortunately, its not the case. People are
> saying that my proposal is bad, and the only
> suggested amendment is to drop it immediately. :)
> 
> But I am very thankful to those who at least did
> some side-activities for me, like discussing my
> use-case and reviewing part of the patches.
> These activities were very valuable, but the real
> problems were never discussed before, proposed
> API was not reviewed, and so on.

It is being tiring to work with your proposal because Rich already
brought the inherent issue of exposing the loader internals for ELF
objects [1] about 10 years ago, Szabolcs and myself are constantly
trying to say that is not good libc interface (regardless it solved
your specific problem), and even Jonathan tried to explore different 
alternatives.

The main problem is no one from glibc is comfortable, including myself,
to maintain and work with this interface. I don't want to maintain
another interface with a lot of corner cases.

Also the way approaching the glibc community, by not listening to 
criticism and ignoring multiple remarks; saying that you have addresses
the comments without any ack; or flooding the maillist with multiple 
version without addressing previous feedback is really, and I want to 
stress the really here, tiring.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=11767

> 
> And with your input I already advanced to the
> point where the API can be split into 2 parts,
> whereas before I thought of dlmem() as a
> thing in itself that can't be split. Now I see
> that RTLD_NORELOC can solve the biggest part
> of the puzzle alone.

Sigh, I really would like to say this is less rude manner but I will
be succinct so you and others can avoid waste time in this manner.
The "dlopen of in-memory ET_DYN or ET_EXEC object" is a *NACK* from
me, even if is split in multiple APIs.  Myself, Szabolsh, and Rich
already explained, but you seemed resolute to not accept it.

A fdlopen or even a dlopen_with_offset could make sense, FreeBSD
at least has fdlopen so it is a precedent of a realworld case.

> 
> 
>> when i said you should write your own loader i expected
>> you to create simple stubs in memory at the location
>> you want them to be and bind them to a library that
>> is loaded by libc (so you can connect your weird world
>> with the libc world).
>>
>> it looked like a simple solution to me, but i still dont
>> know exactly your requirements so in a sense we still
>> don't have a well understood use-case for any of your
>> proposed changes. so maye the use-case is where you
>> should start.
> You mean "restart". :)
> As I already explained it very thoroughly in the
> bugzilla, but you missed that and now its better
> to repeat, since the bugzilla entry had a very
> lengthy discussions afterwards.
> 
> The use-case is a VM with 32bit code inside.
> It has a 4Gb window mapped somewhere in
> a 64bit space (call it VM_window_start).
> The task is very simple: first I find the suitable
> mapping address within a VM address space.
> Such address is within the 0-4Gb boundaries.
> I relocate the solib to that "low" address.
> Unfortunately this is still not visible to the VM,
> because VM's window is at VM_window_start,
> as we remember. So in order for VM-running
> 32bit code to access the solib's data (like .bss,
> .rodata) when it gets a 32bit pointer to such
> data, I also need to map the full duplicate of
> the solib object into the address that looks like:
> 
> dupe_addr = VM_window_start + reloc_addr
> 
> For that I invented the optional dlmem flag
> that doesn't destroy the destination mapping.
> But such flag doesn't survive your requirement
> to get rid of a call-back, so in the next prop
> I'll pass a shm fd to dlmem instead, and an
> optional flag will ask dlmem() to mmap that
> fd as a destination backing-store.
> 
> So I am sure all problems can be solved!
> But of course I can't do that alone, if everyone
> just keeps saying "drop your patches immediately".



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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 18:09                                   ` Adhemerval Zanella Netto
@ 2023-04-13 18:59                                     ` stsp
  2023-04-13 19:12                                       ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-13 18:59 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell

[-- Attachment #1: Type: text/plain, Size: 4419 bytes --]

Hi Adhemerval,

13.04.2023 23:09, Adhemerval Zanella Netto пишет:
> It is being tiring to work with your proposal because Rich already
> brought the inherent issue of exposing the loader internals for ELF
> objects [1] about 10 years ago,

My proposal did not exist 10 years ago.
Maybe we all do not properly document
our proposals or an objections. So let me
ask you to please refer to a particular
comment rather than to some 10 years
ago thread, and then carefully document
how can that be the problem in my patches.

I think its a fair ask, e.g. Szabolcs asked
me for a more precise spec - I write. We
progress. Why can't you write the objections
in a detalization level that is enough to
make a progress?

I admit you probably couldn't do that initially,
because I poorly documented my API. But
when Carlos asked for a more detailed spec,
I did. Now you can express your objections
in a detailed manner, let me even attach
the prev API draft to help you doing that.
Of course this draft will be simplified a lot,
but for such a "generic" objections please
use its current form.

>   Szabolcs and myself are constantly
> trying to say that is not good libc interface (regardless it solved
> your specific problem),

And regardless that it was never even discussed.
Part of the failure is on me: I wrote an API draft
only when Carlos asked, I should probably do that
before anything else. Sorry, I am a novice. But its
written now for quite long, so why nothing changes?


>   and even Jonathan tried to explore different
> alternatives.

I am thankful to Jonathon for doing that.
Unfortunately I am unsatisfied with the proposal
of intercepting glibc's internal mmap()s on a
syscall level, and binding a non-trivial (I mean,
very non-trivial!) logic on them. But I am thankful
he proved this is even possible, as I was confident
in an otherwise.

> The main problem is no one from glibc is comfortable, including myself,
> to maintain and work with this interface.

Which no one, besides Szabolcs, have actually
even looked into? It is attached to this e-mail.
Why not to make the complains direct and precise?


>   I don't want to maintain
> another interface with a lot of corner cases.

I have expressed the plans at removing all the
corner cases that Szabolcs pointed to. If you
point more, I'll definitely take an actions.
Off-loading the biggest part to RTLD_NORELOC
will reduce the proposal considerably, avoid
the callback and most of other complexity, so
why such a prejudice? Why can't we just discuss,
amend, see what happens?

> Also the way approaching the glibc community, by not listening to
> criticism and ignoring multiple remarks;

I wonder if Szabolcs also thinks I ignore his
remarks. If you think I ignore yours, then either
that was a misunderstanding on my part, or
they were not referring to my API proposal
(which is attached), or something else from that
line, but definitely not my reluctance to change
the proposal. I want to address every possible
problem raised, but unfortunately so far I only
see the problems raised by Szabolcs.
He details them well and targets over my API
proposals. Its immediately obvious what does
he mean, and after his last review I spent a
full month (!) just thinking how to get rid of
a call-back, and finally got to this RTLD_NORELOC
proposal as a result.


>   saying that you have addresses
> the comments without any ack;

That's a bit strange. Yes, I do some actions
based on a comments I get, then post the
new RFC and write in a log that I addressed
this and that. If there is a further complain,
i can re-do what I thought to be addressed,
and replace it with something I believe now
addresses the comment. Its an iterative
process, misunderstanding happens, etc.
Of course if the policy is to not write "addressed
this and that comment" before getting an
ACK, then I will no longer do that, and write
"tried to address this and that, hope its now better".


>   or flooding the maillist with multiple
> version without addressing previous feedback is really, and I want to
> stress the really here, tiring.

Ok, should I post the next drafts to the bugzilla
then, if flooding ML is bad?
Its not a problem, really!
You tell me what to do, I do.
Why such a policy things are even becoming
a problems? I didn't mean to offend anyone
by flooding the ML, I'll post to bugzilla, and I
don't want you to be offended on me just because
of that. :)

[-- Attachment #2: 0000-cover-letter.patch --]
[-- Type: text/x-patch, Size: 8231 bytes --]

From 46e5095ebfe63be4dcd813c4237d6a491a3f9768 Mon Sep 17 00:00:00 2001
From: Stas Sergeev <stsp2@yandex.ru>
Date: Mon, 13 Feb 2023 18:15:34 +0500
Subject: [PATCH v10 0/12] implement dlmem() function

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] 107+ messages in thread

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 18:59                                     ` stsp
@ 2023-04-13 19:12                                       ` Adhemerval Zanella Netto
  2023-04-13 19:29                                         ` stsp
  0 siblings, 1 reply; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-04-13 19:12 UTC (permalink / raw)
  To: stsp, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell



On 13/04/23 15:59, stsp wrote:
> Hi Adhemerval,
> 
> 13.04.2023 23:09, Adhemerval Zanella Netto пишет:
>> It is being tiring to work with your proposal because Rich already
>> brought the inherent issue of exposing the loader internals for ELF
>> objects [1] about 10 years ago,
> 
> My proposal did not exist 10 years ago.
> Maybe we all do not properly document
> our proposals or an objections. So let me
> ask you to please refer to a particular
> comment rather than to some 10 years
> ago thread, and then carefully document
> how can that be the problem in my patches.
> 
> I think its a fair ask, e.g. Szabolcs asked
> me for a more precise spec - I write. We
> progress. Why can't you write the objections
> in a detalization level that is enough to
> make a progress?

Sigh, we are going into circles again.

> 
> I admit you probably couldn't do that initially,
> because I poorly documented my API. But
> when Carlos asked for a more detailed spec,
> I did. Now you can express your objections
> in a detailed manner, let me even attach
> the prev API draft to help you doing that.
> Of course this draft will be simplified a lot,
> but for such a "generic" objections please
> use its current form.

The problem is not a poor documented ABI, the ABI itself has inherent
corner cases brought multiple times.  The problem you are trying to
solve would be better served with a custom loader.

> 
> I have expressed the plans at removing all the
> corner cases that Szabolcs pointed to. If you
> point more, I'll definitely take an actions.
> Off-loading the biggest part to RTLD_NORELOC
> will reduce the proposal considerably, avoid
> the callback and most of other complexity, so
> why such a prejudice? Why can't we just discuss,
> amend, see what happens?

The RTLD_NORELOC itself is not a better approach, is just moving the
same corner cases that we brought to a different symbol... against
walking in cycles.

> 
> Ok, should I post the next drafts to the bugzilla
> then, if flooding ML is bad?
> Its not a problem, really!
> You tell me what to do, I do.
> Why such a policy things are even becoming
> a problems? I didn't mean to offend anyone
> by flooding the ML, I'll post to bugzilla, and I
> don't want you to be offended on me just because
> of that. :)

Do not bother, I think the best course of action is just to drop
the RFE.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 19:12                                       ` Adhemerval Zanella Netto
@ 2023-04-13 19:29                                         ` stsp
  2023-04-13 20:02                                           ` Adhemerval Zanella Netto
  0 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-13 19:29 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell

Adhemerval,

14.04.2023 00:12, Adhemerval Zanella Netto пишет:
>> I admit you probably couldn't do that initially,
>> because I poorly documented my API. But
>> when Carlos asked for a more detailed spec,
>> I did. Now you can express your objections
>> in a detailed manner, let me even attach
>> the prev API draft to help you doing that.
>> Of course this draft will be simplified a lot,
>> but for such a "generic" objections please
>> use its current form.
> The problem is not a poor documented ABI, the ABI itself has inherent
> corner cases brought multiple times.

By whom?
Where?
You referred to some 10 years old thread,
where I didn't even participated. Is it too
much to ask to at least point me to a particular
comment in that thread?


>    The problem you are trying to
> solve would be better served with a custom loader.

I wish to have a custom loader.
But so far I don't know how to make it
friendly to glibc. Custom loader needs
to be able to create a link-map, and glibc
currently doesn't have a hook to do that.
I currently only know how to create a
custom loader that works until glibc
changes either struct link_map or struct
rtld_global_ro. Such custom loader can
be done, but it won't be very reliable.

>> I have expressed the plans at removing all the
>> corner cases that Szabolcs pointed to. If you
>> point more, I'll definitely take an actions.
>> Off-loading the biggest part to RTLD_NORELOC
>> will reduce the proposal considerably, avoid
>> the callback and most of other complexity, so
>> why such a prejudice? Why can't we just discuss,
>> amend, see what happens?
> The RTLD_NORELOC itself is not a better approach, is just moving the
> same corner cases that we brought to a different symbol... against
> walking in cycles.

Why not to show these problems?
Szabolcs always does. You always assert on
their existence, as if I have no right to even
ask what they are. Maybe they are obvious, maybe
I have to realize them w/o asking, but I can't,
I am a novice.
Please, pretty please, can you detail those problems?
It would be best if you detail them with the
reference to my drafts - then I'll immediately
understand.


> Do not bother, I think the best course of action is just to drop
> the RFE.
Why is that constructive?
Well is not for Szabolcs, I could suspect its
just me. But now if he points to the problems
promptly, then I am sure its possible to do.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 19:29                                         ` stsp
@ 2023-04-13 20:02                                           ` Adhemerval Zanella Netto
  2023-04-13 20:21                                             ` stsp
                                                               ` (5 more replies)
  0 siblings, 6 replies; 107+ messages in thread
From: Adhemerval Zanella Netto @ 2023-04-13 20:02 UTC (permalink / raw)
  To: stsp, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell



On 13/04/23 16:29, stsp wrote:
> Adhemerval,
> 
> 14.04.2023 00:12, Adhemerval Zanella Netto пишет:
>>> I admit you probably couldn't do that initially,
>>> because I poorly documented my API. But
>>> when Carlos asked for a more detailed spec,
>>> I did. Now you can express your objections
>>> in a detailed manner, let me even attach
>>> the prev API draft to help you doing that.
>>> Of course this draft will be simplified a lot,
>>> but for such a "generic" objections please
>>> use its current form.
>> The problem is not a poor documented ABI, the ABI itself has inherent
>> corner cases brought multiple times.
> 
> By whom?
> Where?
> You referred to some 10 years old thread,
> where I didn't even participated. Is it too
> much to ask to at least point me to a particular
> comment in that thread?

Because it is the same remark bought *multiple* times in this thread [1]
that you keep saying it is not important on the discussion:

"It would be possible to require the caller to arrange all of 
these things, but that's basically offloading A LOT of the ELF 
loading process onto the calling program and I don't think that 
makes for a reasonable public interface for glibc to provide."

And every time someone brought it [2], you just reply that this since it
does not fit in your usercase this is not an acceptable solution.  And
then we kept in circles, with multiple developers saying that this
interface-like is not reasonable, and you saying that it is the only way 
to solve your specific usercase.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=11767#c16
[2] https://sourceware.org/pipermail/libc-alpha/2023-April/146886.html

> 
> 
>>    The problem you are trying to
>> solve would be better served with a custom loader.
> 
> I wish to have a custom loader.
> But so far I don't know how to make it
> friendly to glibc. Custom loader needs
> to be able to create a link-map, and glibc
> currently doesn't have a hook to do that.
> I currently only know how to create a
> custom loader that works until glibc
> changes either struct link_map or struct
> rtld_global_ro. Such custom loader can
> be done, but it won't be very reliable.

You can just use glibc, apply your patches, and use it instead.

> 
>>> I have expressed the plans at removing all the
>>> corner cases that Szabolcs pointed to. If you
>>> point more, I'll definitely take an actions.
>>> Off-loading the biggest part to RTLD_NORELOC
>>> will reduce the proposal considerably, avoid
>>> the callback and most of other complexity, so
>>> why such a prejudice? Why can't we just discuss,
>>> amend, see what happens?
>> The RTLD_NORELOC itself is not a better approach, is just moving the
>> same corner cases that we brought to a different symbol... against
>> walking in cycles.
> 
> Why not to show these problems?
> Szabolcs always does. You always assert on
> their existence, as if I have no right to even
> ask what they are. Maybe they are obvious, maybe
> I have to realize them w/o asking, but I can't,
> I am a novice.
> Please, pretty please, can you detail those problems?
> It would be best if you detail them with the
> reference to my drafts - then I'll immediately
> understand.

Because every time someone bring the corner cases you just dismiss saying 
that either the person does not understand you [1] and it is up to review
prove you are wrong; or you dismiss saying that the remarks are not
applicable.

[3] https://sourceware.org/pipermail/libc-alpha/2023-March/146729.html
[4] https://sourceware.org/pipermail/libc-alpha/2023-March/146717.html

> 
> 
>> Do not bother, I think the best course of action is just to drop
>> the RFE.
> Why is that constructive?
> Well is not for Szabolcs, I could suspect its
> just me. But now if he points to the problems
> promptly, then I am sure its possible to do.

Because we are not moving anywhere, if you have not already get it
the replies are to try to show you the problems with the RFE proposal,
not to move toward having dlmem-like interface incorporated.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 20:02                                           ` Adhemerval Zanella Netto
@ 2023-04-13 20:21                                             ` stsp
  2023-04-13 20:57                                             ` stsp
                                                               ` (4 subsequent siblings)
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-13 20:21 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell

Adhemerval,

14.04.2023 01:02, Adhemerval Zanella Netto пишет:
> Because it is the same remark bought *multiple* times in this thread [1]
> that you keep saying it is not important on the discussion:
>
> "It would be possible to require the caller to arrange all of
> these things, but that's basically offloading A LOT of the ELF
> loading process onto the calling program and I don't think that
> makes for a reasonable public interface for glibc to provide."

That cr^H^Hcomment was indeed brought
to me several times. But there is a second part
of a deal, which is to explain how it is ever
relevant to my patches. Maybe it was not
possible before I wrote a proper spec draft,
but its definitely possible now.

> And every time someone brought it [2], you just reply that

... that its a bu^H^Hirrelevant comment, having
ZERO relevance to my patches?
Well, its true. Now when the patches are properly
specced, I think I have a right to ask why that
comment is even raised at all. Does it have anything
to do with my spec draft? No. Does it have anything
to do with my patches? No. Does it have anything
to do with my usage examples? No. Does it anything
to do with some theoretical usage examples of my
API? Still no!
Does it have to do with anything at all? No.
What should I do? :(

Oh, idea!
Szabolcs never lies, I trust him.
If he says I should address (or even read) that
comment, and that its a comment that is "somehow"
relevant to my API and patches, then I'll bring my
patches down.
But so far I was only seeing that comment as an
attempt to discredit my work by any price, even
by the pure hideous lie. :(


>   this since it
> does not fit in your usercase this is not an acceptable solution.

And I never said anything like this, too.


>    And
> then we kept in circles, with multiple developers saying that this
> interface-like is not reasonable, and you saying that it is the only way
> to solve your specific usercase.

No.
We are walking in circles with intentionally malicious
argument towards my work, because you wanted to
put my patches down by any price.
This is unacceptable.

Szabolcs will not repeat that malicious argument,
or if he will, I'll put my patches down.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 20:02                                           ` Adhemerval Zanella Netto
  2023-04-13 20:21                                             ` stsp
@ 2023-04-13 20:57                                             ` stsp
  2023-04-14  7:07                                             ` stsp
                                                               ` (3 subsequent siblings)
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-13 20:57 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell


14.04.2023 01:02, Adhemerval Zanella Netto пишет:
> Because every time someone bring the corner cases you just dismiss saying

Compare that with corner cases brought
by Szabolcs. Have I ignored a single one?

> that either the person does not understand you [1]

So was a hope.
I didn't know it was intentional.


>   and it is up to review
> prove you are wrong;
Why do you intentionally omit the posting
where I actually provide a proof, after getting
off of an initial shock?

https://sourceware.org/pipermail/libc-alpha/2023-March/146742.html


>   or you dismiss saying that the remarks are not
> applicable.

Yes.
The quoted remarks are not applicable.
I proved that, see above URL.
And I explicitly asked a few times, do you
need more proofs, and if so - what kind of?
Its still relevant - I can code up any proof
you want, if the above obvious proof wasn't
sufficient.
That conspiracy theory could only work if not
for Szabolcs who have started the actual
review, and not for the spec drafts that I published,
etc. After all these I was hoping you will no
longer bring that to public, but you did. :(


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-03-31 11:04       ` stsp
@ 2023-04-13 21:17         ` Carlos O'Donell
  2023-04-13 21:58           ` stsp
                             ` (5 more replies)
  0 siblings, 6 replies; 107+ messages in thread
From: Carlos O'Donell @ 2023-04-13 21:17 UTC (permalink / raw)
  To: stsp, libc-alpha, Jonathon Anderson

On 3/31/23 07:04, stsp wrote:
> Hi Carlos, Jonathon.
> 
> 29.03.2023 23:13, Carlos O'Donell пишет:
>> The most important thing is the reasons for the change and that should come first.
> 
> Done, please see the attachment.

Your v10 cover letter is great and makes it completely clear what it is
you want to implement.

However, the API itself is just not acceptable to me as an abstraction
that is generic enough to warrant inclusion in glibc. This is a design
decision I'm making as a developer.

In this thread you don't have consensus from Adhemerval, or myself as
core glibc developers. So this patch won't move forward towards inclusion
until you can get our consensus. This *blocks* this patch from ever being
included in glibc per consensus rules:
https://sourceware.org/glibc/wiki/Consensus

You don't have consensus from Zach Weinberg (GNU Autoconf maintainer) who
has also responded on the thread and is a GNU Toolchain contributor.

Likewise you don't have conses from Rich Felker, who as an alternative
libc developer would need to consider implementing something similar.

I don't see any way to salvage this API as it is, and while it might
serve your needs, the core C library is not designed to serve all needs,
but to serve the most generic needs.

The most generic needs can be seen by existing prior art like fdlopen
or dlopen_with_offset(), and they have been implemented.

>> This needs to explain the workload that requires the API.
>>
>> Why that workload is generic.
>>
>> Any core system library, like glibc, is not the place for *all* APIs, but the
>> place for the most generic building blocks for ISO C, POSIX, BSD, GNU, and
>> Linux APIs.
> 
> I hope I've got that suggestion rightly.
> Please see if my current description is
> adequate.

Not quite. A workload is application that would use the API in a specific way.

For example you don't mention which workloads would be improved by using these
APIs? Would an application use the APIs to accelerate or handling plugin loading?
What about loading DSOs from NVRAM? What about replacing all the vDSO handling
with the API?

> And on the other front...
> I studied and documented (in the attachment)
> all the cases where my impl fails to arrange
> an elf segments properly... I have to admit
> such cases were possible. :(
> I documented them and their mitigations in
> the "Limitations" section.
> Let me know if this is now adequate.

These are well documented. You've really done a good job of calling them out.

In summary:

- This API as designed does not have consensus. Posting v10 does not fix the
  consensus problem.

- I suggest dropping the feature for glibc. If there are other things you
  would like to work on in glibc, please reach out again.

-- 
Cheers,
Carlos.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 21:17         ` Carlos O'Donell
@ 2023-04-13 21:58           ` stsp
  2023-04-13 22:08           ` stsp
                             ` (4 subsequent siblings)
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-13 21:58 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha, Jonathon Anderson,
	Adhemerval Zanella Netto

Hi Carlos,

14.04.2023 02:17, Carlos O'Donell пишет:
> In this thread you don't have consensus from Adhemerval, or myself as
> core glibc developers.

Have you forgot to reply to this e-mail:

https://sourceware.org/pipermail/libc-alpha/2023-March/146742.html

I sent you a proof, I asked multiple times
(including privately) was a proof adequate.
But you haven't replied, and haven't dropped
that malicious "elf parsing" argument.
Can you do that now?
Was my proof sufficient, or should I code
up another one?


>   So this patch won't move forward towards inclusion
> until you can get our consensus. This *blocks* this patch from ever being
> included in glibc per consensus rules:

You perfectly know what _actually_ blocks it.
And its a small and simple fact: you have never
replied to my proof, that I sent to you by your
own request. You haven't put down the malicious
"elf parsing" argument, and haven't said the proof
was insufficient (or sufficient).

> You don't have consensus from Zach Weinberg (GNU Autoconf maintainer) who
> has also responded on the thread and is a GNU Toolchain contributor.

I wasn't CCed with this, and doing a "thread view"
via web interface doesn't allow me to find his e-mail.
Could you please provide an URL?

> Likewise you don't have conses from Rich Felker, who as an alternative
> libc developer would need to consider implementing something similar.

The API is in draft.
Draft can't be in consensus with everyone,
especially until you tell me whether the proof
was sufficient or not.

> I don't see any way to salvage this API as it is,

As it is it shouldn't be included.
Its a draft that should be _discussed_.
Its discussed only by Szabolcs, and very
unfortunately there are also the aforementioned
affairs going on about your "elf parsing" thing.


> Not quite. A workload is application that would use the API in a specific way.
>
> For example you don't mention which workloads would be improved by using these
> APIs? Would an application use the APIs to accelerate or handling plugin loading?
> What about loading DSOs from NVRAM?

OK I'll try to summarize those in the next draft.
Basically the estimation is that this API is very
much comparable with the existing dlopen() in
speed, as it doesn't use slow functions like read()
or memcpy() (well, memcpy() behavior exists
but its not a default one). Whether it increases
or decreases the syscall amount per DSO is
difficult to say (will need to measure), but even
if it does, the overhead would be a couple of
syscalls per DSO.


>   What about replacing all the vDSO handling
> with the API?

My estimation is that you will measure no changes.
Of course the final draft will need the benchmark
numbers, but its too early to add just yet.


> - I suggest dropping the feature for glibc. If there are other things you
>    would like to work on in glibc, please reach out again.
Why not to reply to the aforementioned e-mail
to at least make such a statement fair?

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 21:17         ` Carlos O'Donell
  2023-04-13 21:58           ` stsp
@ 2023-04-13 22:08           ` stsp
  2023-04-13 22:50           ` stsp
                             ` (3 subsequent siblings)
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-13 22:08 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha, Jonathon Anderson,
	Adhemerval Zanella Netto, Zack Weinberg

OK, I've found the mail from Zack Weinberg,
it wasn't CCed to me. Thanks for telling about
it. Found it in an archive.

14.04.2023 02:17, Carlos O'Donell пишет:
> If there are other things you
>    would like to work on in glibc, please reach out again.
Of course there are:
https://sourceware.org/pipermail/libc-alpha/2023-April/147243.html
Would be good to consider that spec as well.

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 21:17         ` Carlos O'Donell
  2023-04-13 21:58           ` stsp
  2023-04-13 22:08           ` stsp
@ 2023-04-13 22:50           ` stsp
  2023-04-14 16:15           ` Autoconf maintenance (extremely tangential to Re: [PATCH v9 0/13] implement dlmem() function) Zack Weinberg
                             ` (2 subsequent siblings)
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-13 22:50 UTC (permalink / raw)
  To: Zack Weinberg
  Cc: Carlos O'Donell, libc-alpha, Jonathon Anderson,
	Adhemerval Zanella Netto, Szabolcs Nagy


[-- Attachment #1.1: Type: text/plain, Size: 6122 bytes --]

Hi Zack, thanks for a reply.
Have you forgot to CC the e-mail to me,
or was it some spam filtering? Carlos have
just told me about it.
I'll use copy/paste from web interface to reply,
and will monitor the web archive for a few days
in case your mails to me are blocked somewhere.


> Let me try to highlight specific issues from the feedback you've been
> getting.  It appears to me that your *API* proposal -- not any details
> of its implementation, the API itself -- is unacceptable *BECAUSE*:
>
> * It introduces a callback from the dynamic loader into user code, that
>    is to be run with an internal lock held. glibc does already have
>    callbacks like that, but we do not want to introduce any more of
>    them, period.
> * It assumes that the dynamic loader will always, in the future, operate
>    by creating a large initial mapping and then modifying protection
>    flags on it.
Yes, those are the totally valid concerns
raised by Szabolcs. They were very difficult
to address actually, and eventually I came to
this proposal:
https://sourceware.org/pipermail/libc-alpha/2023-April/147243.html

It is needed to address exactly those, but
it may not be obvious yet how exactly they
are related. I think this is not a problem, you'll
see that in a next dlmem() draft. These problems
will be solved. Most of dlmem() can be off-loaded
to either proposed RTLD_NORELOC or something
similar.

> * It assumes that it the dynamic loader is, and will always be, capable
>    of taking a pointer to a block of memory -- no matter how it was
>    originally allocated -- and then chopping its virtual memory map up to
>    satisfy the requirements of the embedded ELF program header table,
>    without breaking anything else.
I think you are talking about a premap callback
here? It will be dropped per comment 1 of yours,
so I think this is mostly a duplicate.

> [On this specific note, I foresee
>    someone trying to write a dynamic module into memory allocated with
>    memalign() or even plain old malloc(); calling dlmem() on this is very
>    likely to apply PROT_NONE to address ranges containing malloc
>    metadata!]
No-no, it never changes the protection
of a source buffer! Nothing to worry about
here. Also there are some recommendations
in an API draft what mappings are supported
as a source and what not - using memalign()
space is very much unrecommended unless
you know what you do (see api draft for details).
But the protections are of course not altered.

> For instance, maybe we could have an fdlopen variant that
> called callback functions instead of mmap() and mprotect().  Unlike
> your dlmem_premap_t, I think it ought to be possible to avoid doing
> those specific operations with any locks held.
But what such a callback would do?
Plain read()?


So.
You are welcome to this lengthy thread. :)
I am attaching an API draft in case you are
curious about a details, but it was not yet
updated to the removal of a callback.
Sorry about that, just take my word on that
for now. :)

There are a few things that you miss about
the proposed design (as every newcomer
to this thread does), so let me show you an
examples of fdlopen() and dlopen_with_offset()
implemented on top of dlmem(). Most of things,
like eg the source buffer usage, will be more
clear after that:

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;
}

If we run that example, then in /proc/self/maps
we will see the correct references to an elf file:

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

As you an see, there is no reference to the
full file, just to the elf segments. That's
because the above code just munmap()s the source
buffer after dlmem() returns. Its protection
was never changed.

Now, dlopen_with_offset() is slightly more difficult,
but is suggested for a read too:

/* 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;
}

[-- Attachment #2: 0000-cover-letter.patch --]
[-- Type: text/x-patch, Size: 8231 bytes --]

From 46e5095ebfe63be4dcd813c4237d6a491a3f9768 Mon Sep 17 00:00:00 2001
From: Stas Sergeev <stsp2@yandex.ru>
Date: Mon, 13 Feb 2023 18:15:34 +0500
Subject: [PATCH v10 0/12] implement dlmem() function

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] 107+ messages in thread

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 20:02                                           ` Adhemerval Zanella Netto
  2023-04-13 20:21                                             ` stsp
  2023-04-13 20:57                                             ` stsp
@ 2023-04-14  7:07                                             ` stsp
  2023-04-14  7:36                                             ` stsp
                                                               ` (2 subsequent siblings)
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-14  7:07 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell, Zack Weinberg

Hi Adhemerval,

14.04.2023 01:02, Adhemerval Zanella Netto пишет:
> Because it is the same remark bought *multiple* times in this thread [1]
> that you keep saying it is not important on the discussion:
>
> "It would be possible to require the caller to arrange all of
> these things, but that's basically offloading A LOT of the ELF
> loading process onto the calling program and I don't think that
> makes for a reasonable public interface for glibc to provide."
OK, I got off of an another shock from this, and am
absolutely calm again, just having a headache...
So lets get to the facts.

1.  There is now a written spec that explicitly
      says what is required. It says exactly how
      the solib should be put to the memory buffer,
      what types of mapping are supported, what
      types unsupported, how to dlmem() from
      aligned buffer, from unaligned buffer, from
      file-mapped buffer, and so on. So the whole
      "A LOT of the ELF loading process" is now
      fully specified and detailed.

2. The quote you did above, doesn't reveal the
     whole story. This URL does:

https://inbox.sourceware.org/libc-alpha/b6a96687-9a4b-414f-849a-45a305898274@redhat.com/

In this e-mail Carlos does claims that directly
contradict my spec. And based on them, not
based on my spec (!!!) he concludes that my
API is unacceptable. How can the one conclude
some API is unacceptable, writing his own (wrong)
spec of it? That's very strange, isn't it?

If my spec clearly say you only need to mmap()
the solib, and some alternative spec says you
need to also arrange elf segments by hands
before calling dlmem(), then how can this even
be the truth if we discuss _my_ spec? Maybe
Carlos reverse-engineered my implementation
of a spec, and found that it doesn't conform to
my own spec? And wrote his own one that matches
my implementation? I very much doubt so, but
even if this is true, then why not to point me to
such a bug in an implementation, and ask to
make it so that it matches _my_ spec? Why not
to ask me to make the loader itself to arrange
an elf segments, if it is not the case right now?
(but of course it is, that was proved many times)

So overall, I discuss my spec, Carlos discusses
his own. Why is it productive?

If it is widely believed my impl doesn't match
my own spec, then can we for a moment forget
about an impl, and discuss only my spec? It
clearly says that mapping the segment is a
loader's responsibility. I even explicitly wrote
that the mmapped image should not be modified,
or dlmem() won't work. Do we discuss _that_
spec, or some other spec of Carlos? That's what
I'd like to hear an answer to.

> And every time someone brought it [2], you just reply that this since it
> does not fit in your usercase this is not an acceptable solution.
This is yet another misunderstanding.
I say that when people offer me memfd+dlopen
replacement. That doesn't work for me, but it
has zero relevance to the discussed "Carlos's
spec of my API".

Now. I apologize if my yesterday's mails were
a bit rough. I hope we can all be technical and
I wish we all can always be calm. But when my
spec is replaced and another one is being used
to "justify" my work, I am really feeling quite bad.

Lets get technical, lets stick to facts and to my spec,
please. :)
If my spec says no elf parsing or segment
arranging is needed, then lets _believe_ that
my implementation matches it, please.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 20:02                                           ` Adhemerval Zanella Netto
                                                               ` (2 preceding siblings ...)
  2023-04-14  7:07                                             ` stsp
@ 2023-04-14  7:36                                             ` stsp
  2023-04-14 11:30                                             ` stsp
  2023-04-14 19:04                                             ` proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function) stsp
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-14  7:36 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell, Zack Weinberg


14.04.2023 01:02, Adhemerval Zanella Netto пишет:
> Because every time someone bring the corner cases you just dismiss saying
> that either the person does not understand you [1] and it is up to review
> prove you are wrong; or you dismiss saying that the remarks are not
> applicable.
Yes, because I wrote into my spec that this
particular remark in not applicable. In particular
I detailed the process of loading a solib, and
stated that the initially mmapped buffer should
not be modified.
Whatever I write to a spec, should be regarded
as "the truth by definition" because we discuss
_my_ spec, not Carlos's spec.
I can't be even asked to "prove what I wrote into
a spec" - its an incorrect requirement. But I
did an attempt to prove that my _implementation_
is conforming to my spec. No one "noticed"...
But it doesn't even matter, because we are
discussing a spec, not an implementation!
So nothing from my spec should be a subject
of some "proving". And per my spec, no "elf
parsing" is needed, and no manual arranging
of PT_LOAD segments is needed - dlmem()
does it itself. This can't be put under any disbelief,
because its a spec!

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 20:02                                           ` Adhemerval Zanella Netto
                                                               ` (3 preceding siblings ...)
  2023-04-14  7:36                                             ` stsp
@ 2023-04-14 11:30                                             ` stsp
  2023-04-14 19:04                                             ` proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function) stsp
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-04-14 11:30 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell, Zack Weinberg

Adhemerval,

14.04.2023 01:02, Adhemerval Zanella Netto пишет:
> Because it is the same remark bought *multiple* times in this thread [1]
> that you keep saying it is not important on the discussion:
>
> "It would be possible to require the caller to arrange all of
> these things, but that's basically offloading A LOT of the ELF
> loading process onto the calling program and I don't think that
> makes for a reasonable public interface for glibc to provide."
Now let me quote the relevant part of _my_
spec:

- 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.


That's what _my_ spec says.
Why can't we just settle on a fact that anything
that contradicts my spec, be it taken from some
10-years-old thread or from elsewhere, cannot
be used as an argument when judging my API?
Why can't we settle on a fact that even the
attempts to do so are unacceptable, especially
the repetitive attempts?
We discuss my spec, so replacing its parts during
the discussion, is counter-productive.

> Because every time someone bring the corner cases you just dismiss saying
> that either the person does not understand you [1] and it is up to review
> prove you are wrong;
Because the reviewer must not operate on
some other spec when judging my spec. If
the reviewer thinks his spec describes my
proposed implementation better than my
own spec, then he should better actually try
to prove that. But at the end I presented my
own proof that my impl is conforming:
https://sourceware.org/pipermail/libc-alpha/2023-March/146742.html

And now the question is simple: what else
should be done on my side to have that
case finally resolved? Code up some other
proof? Write more clearly in a spec that no
elf parsing is needed?
Its a simple question, just what should I do?
You tell - I do.


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

* Autoconf maintenance (extremely tangential to Re: [PATCH v9 0/13] implement dlmem() function)
  2023-04-13 21:17         ` Carlos O'Donell
                             ` (2 preceding siblings ...)
  2023-04-13 22:50           ` stsp
@ 2023-04-14 16:15           ` Zack Weinberg
  2023-04-14 20:24             ` Carlos O'Donell
  2023-05-08 15:05           ` [PATCH v9 0/13] implement dlmem() function stsp
  2023-05-19  7:26           ` stsp
  5 siblings, 1 reply; 107+ messages in thread
From: Zack Weinberg @ 2023-04-14 16:15 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha

On 2023-04-13 5:17 PM, Carlos O'Donell via Libc-alpha wrote:
> Zach Weinberg (GNU Autoconf maintainer)
I feel that referring to me as the GNU Autoconf maintainer is likely to 
mislead people.

At present, GNU Autoconf does not _have_ any active maintainers, and 
this is not likely to change anytime soon.  I _wanted_ to put out a new 
Autoconf release (to better support clang 16.0.0, which has turned off 
support for implicit function declarations in its default mode) months 
ago and it hasn't been possible because neither I nor anyone else has 
the time to make it happen.

zw

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

* proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function)
  2023-04-13 20:02                                           ` Adhemerval Zanella Netto
                                                               ` (4 preceding siblings ...)
  2023-04-14 11:30                                             ` stsp
@ 2023-04-14 19:04                                             ` stsp
  2023-05-01 23:11                                               ` Zack Weinberg
  5 siblings, 1 reply; 107+ messages in thread
From: stsp @ 2023-04-14 19:04 UTC (permalink / raw)
  To: Adhemerval Zanella Netto, Szabolcs Nagy, Rich Felker
  Cc: libc-alpha, janderson, Carlos O'Donell, Zack Weinberg

[-- Attachment #1: Type: text/plain, Size: 2801 bytes --]

Hello,

14.04.2023 01:02, Adhemerval Zanella Netto пишет:
> "It would be possible to require the caller to arrange all of
> these things, but that's basically offloading A LOT of the ELF
> loading process onto the calling program and I don't think that
> makes for a reasonable public interface for glibc to provide."
OK, in this case I am going to provide
a very detailed, reproducible and undisputable
proof that the above quote is false.

Attached is the small patch that can be
applied on top of my v10 patch-set to verify
the demo I present here.

The demo I present here, shows the "trace"
of the dlmem() usage, by sampling the
/proc/self/maps at needed points. Namely,
before dlmem(), immediately after dlmem(),
and after the fdlopen-over-dlmem finished.

That demo clearly and unambiguously demonstrates
how dlmem() transforms the raw solib image
into the "loaded" image, i.e. where all the
PT_LOAD sections are correctly laid out.

The output of that demo will be located in
a path: <glibc-build-dir>/dlfcn/tst-dlmem-extfns.out
Everyone can apply the v10+attached patch
and verify my results on his build.

And here it is:

$ cat tst-dlmem-extfns.out

before dlmem
7f5210ca8000-7f5210cad000 r--p 00000000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so

after dlmem
7f5210ca3000-7f5210ca4000 r--p 00000000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca4000-7f5210ca5000 r-xp 00001000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca5000-7f5210ca6000 r--p 00002000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca6000-7f5210ca7000 r--p 00002000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca7000-7f5210ca8000 rw-p 00003000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca8000-7f5210cad000 r--p 00000000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so

post fdlopen
7f5210ca3000-7f5210ca4000 r--p 00000000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca4000-7f5210ca5000 r-xp 00001000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca5000-7f5210ca6000 r--p 00002000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca6000-7f5210ca7000 r--p 00002000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so
7f5210ca7000-7f5210ca8000 rw-p 00003000 00:29 18840304 
/home/stas/src/glibc-dlmem/build/dlfcn/glreflib1.so


PS: yes, I realize nothing can be changed.
But I need to make sure the best possible and
the most obvious proof is publically available,
so that its not to look as if I've given up to
this "wonderful argument that was brought
to me so many times". You got all the proves,
and also more than once.

[-- Attachment #2: a.diff --]
[-- Type: text/x-patch, Size: 1639 bytes --]

diff --git a/dlfcn/tst-dlmem-extfns.c b/dlfcn/tst-dlmem-extfns.c
index 3b98a6e859..e35bdcaeb1 100644
--- a/dlfcn/tst-dlmem-extfns.c
+++ b/dlfcn/tst-dlmem-extfns.c
@@ -36,13 +36,20 @@ fdlopen (int fd, int flags)
   off_t len;
   void *addr;
   void *handle;
+  char cmd[256];
 
   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;
+  printf ("\nbefore dlmem\n");
+  snprintf (cmd, sizeof(cmd), "grep glreflib1.so /proc/%i/maps", getpid());
+  system (cmd);
   handle = dlmem (addr, len, flags, NULL);
+  printf ("\nafter dlmem\n");
+  snprintf (cmd, sizeof(cmd), "grep glreflib1.so /proc/%i/maps", getpid());
+  system (cmd);
   munmap (addr, len);
   return handle;
 }
@@ -111,8 +118,8 @@ do_test (void)
   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);
+//  printf ("unaligned buf gives %s\n", dlerror ());
+//  TEST_COMPARE (errno, EINVAL);
 
   fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
   if (fd == -1)
@@ -128,10 +135,11 @@ do_test (void)
 
   /* Check that the lib is properly mmap()ed, rather than memcpy()ed.
      This may fail on linux kernels <5.13. */
+  printf ("\npost fdlopen\n");
   snprintf (cmd, sizeof(cmd), "grep glreflib1.so /proc/%i/maps", getpid());
   rc = system (cmd);
   TEST_COMPARE (rc, 0);
-
+return 0;
   sym = dlsym (handle, "ref1");
   if (sym == NULL)
     error (EXIT_FAILURE, 0, "dlsym failed");

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

* Re: Autoconf maintenance (extremely tangential to Re: [PATCH v9 0/13] implement dlmem() function)
  2023-04-14 16:15           ` Autoconf maintenance (extremely tangential to Re: [PATCH v9 0/13] implement dlmem() function) Zack Weinberg
@ 2023-04-14 20:24             ` Carlos O'Donell
  2023-04-14 20:40               ` Zack Weinberg
  0 siblings, 1 reply; 107+ messages in thread
From: Carlos O'Donell @ 2023-04-14 20:24 UTC (permalink / raw)
  To: Zack Weinberg, libc-alpha, Florian Weimer, Frederic Berat

On 4/14/23 12:15, Zack Weinberg wrote:
> On 2023-04-13 5:17 PM, Carlos O'Donell via Libc-alpha wrote:
>> Zach Weinberg (GNU Autoconf maintainer)
> I feel that referring to me as the GNU Autoconf maintainer is likely
> to mislead people.

How would you like to be described?

I struggled for a term to describe your role in the ecosystem of packages around
the toolchain space.

I often quote your "A Maintenance Programmer’s View of GCC" (GCC Summit 2003)
around incomplete transitions :-)

> At present, GNU Autoconf does not _have_ any active maintainers, and
> this is not likely to change anytime soon.  I _wanted_ to put out a
> new Autoconf release (to better support clang 16.0.0, which has
> turned off support for implicit function declarations in its default
> mode) months ago and it hasn't been possible because neither I nor
> anyone else has the time to make it happen.

Thanks for that update.

I think that a new Autoconf release is something that the community would want,
particularly as Fedora and Gentoo move towards Modern C:

https://fedoraproject.org/wiki/Changes/PortingToModernC

-- 
Cheers,
Carlos.


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

* Re: Autoconf maintenance (extremely tangential to Re: [PATCH v9 0/13] implement dlmem() function)
  2023-04-14 20:24             ` Carlos O'Donell
@ 2023-04-14 20:40               ` Zack Weinberg
  0 siblings, 0 replies; 107+ messages in thread
From: Zack Weinberg @ 2023-04-14 20:40 UTC (permalink / raw)
  To: libc-alpha, Carlos O'Donell, Florian Weimer, Frederic Berat

On 2023-04-14 4:24 PM, Carlos O'Donell via Libc-alpha wrote:
> On 4/14/23 12:15, Zack Weinberg wrote:
>> On 2023-04-13 5:17 PM, Carlos O'Donell via Libc-alpha wrote:
>>> Zach Weinberg (GNU Autoconf maintainer)
>> I feel that referring to me as the GNU Autoconf maintainer is 
>> likely to mislead people.
> 
> How would you like to be described?

As an individual contributor to many GNU packages, who doesn't have time
to pitch in nearly as much as he'd like.

With regard to autoconf, I may have been the most recent person who
managed to put on a show, and that does mean I feel responsible for
keeping the monkeys fed for the time being, but I have neither time
nor plans to put the circus back into regular operation.

> I think that a new Autoconf release is something that the community 
> would want, particularly as Fedora and Gentoo move towards Modern C:

Yah.  But that conversation is already happening over on autoconf's
mailing lists and is fully off-topic for libc-alpha.

zw

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

* Re: proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function)
  2023-04-14 19:04                                             ` proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function) stsp
@ 2023-05-01 23:11                                               ` Zack Weinberg
  2023-05-02  5:48                                                 ` stsp
  2023-05-02  6:24                                                 ` stsp
  0 siblings, 2 replies; 107+ messages in thread
From: Zack Weinberg @ 2023-05-01 23:11 UTC (permalink / raw)
  To: stsp; +Cc: GNU libc development, Adhemerval Zanella, Carlos O'Donell

This discussion seems to have died down in the past two weeks but I'm
going to make one last attempt to bridge the divide.  I would hate to
see everyone walk away from this with a bad taste in their mouth.  Note
cc: list aggressively pruned.

On Fri, Apr 14, 2023, at 3:04 PM, stsp via Libc-alpha wrote:
> 14.04.2023 01:02, Adhemerval Zanella Netto пишет:
>> "It would be possible to require the caller to arrange all of these
>> things, but that's basically offloading A LOT of the ELF loading
>> process onto the calling program and I don't think that makes for a
>> reasonable public interface for glibc to provide."
> OK, in this case I am going to provide a very detailed, reproducible
> and undisputable proof that the above quote is false.

Here's the thing: stsp, no one is intentionally trying to spread lies
about your patch.  You have misunderstood Adhemerval here.  He was *not*
saying that *your patch* required the caller to parse ELF headers
themselves.  He was saying that your patch does a *different*
unacceptable thing -- running user-supplied code with the loader lock
held, if I understood correctly -- and then he was trying to warn you
that any *alternative* patch you might come up with would *also* be
unacceptable if it required the caller to parse the DLL themselves.

Do you understand the difference between what you thought Adhemerval was
saying, and what I am saying he was trying to communicate?  Please
confirm, or else describe why you don't see a difference so I can try to
clarify again.

Now. Another thing you must understand before you send in any further
revisions of the patch is that glibc is extremely reluctant to add new
APIs.  Witness how much we resisted adding strlcpy, a single function
with a clear specification and no interdependencies with any other
part of the C library.  This is why you are getting what probably
seems to you like an absurd level of negative feedback.  We aren't
trying to deny that you have a legitimate need, and we aren't saying
you haven't managed to come up with a proof of concept implementation
of how that need could be solved -- and yet we ARE saying, no, still
not good enough.

We do this because once we add something, we are stuck with it
*forever*. Not even if the C committee itself deletes something from the
standard do we drop it.  You can *still*, today, write a new program
that uses gets().  And we also do this because GNU libc is loaded into
the address space of *every program* running on (GNU/)Linux.  You're
asking to take up a chunk of (virtual) RAM in *every program* in order
to make your one specific program simpler.  This *will* make at least a
few programs' L1 cache utilization worse.  Not by much, but this happens
every time we add something and it adds up.

---

Having said all that, I can imagine other uses for dlmem() or something like it, besides your specific program, so let me try to meet you in the middle here.  Suppose we gave you *this* API:

/** Report the total amount of virtual address space required in order to load
 *  the shared object referred to by FD.  FD must be a readable and seekable
 *  file descriptor.  Returns a whole number of pages, or zero on error
 *  (in which case errno will provide details).
 */
size_t dl_total_vsize(int fd);

/** Load the shared object referred to by FD into this process's address
 *  space.  FD must be a readable and seekable file descriptor.
 *
 *  If LOAD_ADDR is not NULL, it specifies the address at which to load
 *  the shared object.  If this is not possible, for instance if there
 *  are already memory mappings in the range from LOAD_ADDR to LOAD_ADDR
 *  plus dl_total_vsize(FD), the operation will fail.  LOAD_ADDR must be
 *  a multiple of the system page size.
 *
 *  MODE accepts the same set of RTLD_* flags as are documented for dlopen(),
 *  plus one more: RTLD_READ directs the dynamic loader to read the shared
 *  object into the process's address space as-if with read() rather than
 *  with mmap().  When this flag is used, LOAD_ADDR may not be NULL, and must
 *  already point to at least dl_total_vsize(FD) bytes of writable memory.
 *  The dynamic loader will take ownership of that range of addresses and
 *  may subsequently (e.g. upon a call to dlclose()) deallocate them as-if
 *  with munmap().  Furthermore, the dynamic loader will change virtual
 *  memory protection settings for sub-ranges of this space, so that
 *  (for instance) the text segment of the shared object is read-only,
 *  as-if by calling mprotect().
 */
void *dl_fdopen(int fd, int mode, void *load_addr);

What would still be missing for your use case?

zw

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

* Re: proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function)
  2023-05-01 23:11                                               ` Zack Weinberg
@ 2023-05-02  5:48                                                 ` stsp
  2023-05-08 16:00                                                   ` stsp
  2023-05-02  6:24                                                 ` stsp
  1 sibling, 1 reply; 107+ messages in thread
From: stsp @ 2023-05-02  5:48 UTC (permalink / raw)
  To: Zack Weinberg
  Cc: GNU libc development, Adhemerval Zanella, Carlos O'Donell

[-- Attachment #1: Type: text/plain, Size: 10381 bytes --]

Hi Zack,

02.05.2023 04:11, Zack Weinberg пишет:
> This discussion seems to have died down in the past two weeks but I'm
> going to make one last attempt to bridge the divide.  I would hate to
> see everyone walk away from this with a bad taste in their mouth.  Note
> cc: list aggressively pruned.

The discussion have died down because I
went implementing the RTLD_NORELOCATE
proposal - a replacement to the dlmem()'s
one. Currently its almost completed and I
will draft its spec at the bottom of this e-mail.


> Here's the thing: stsp, no one is intentionally trying to spread lies
> about your patch.  You have misunderstood Adhemerval here.  He was *not*
> saying that *your patch* required the caller to parse ELF headers
> themselves.  He was saying that your patch does a *different*
> unacceptable thing -- running user-supplied code with the loader lock
> held,

Firstly, let me note that we are mis-communicating.
I haven't received your very first e-mail on that
topic (Carlos told me later about it), and you seem
to have not received any (of many) mails I sent
to you both privately and on-list.
So please confirm the communication is OK now.

Now let me clarify.
Running a call-back with the lock held, was
the problem spelled out by Szabolcs - the only
person who did an actual review. That immediately
set me back to the scratch-pads, and I'll soon
introduce the RTLD_NORELOCATE proposal that
doesn't have that problem. So let me put that
straightly: no single problem was ever ignored
or misunderstood on my side. The valid problems
pointed out by Szabolcs, were discussed and
will be addresses (as I told you many times in
the private e-mails, but it seems you received
none of them).

Unfortunately there was also an activity on
"punishing me" running in parallel by others.
It started from this e-mail:
https://inbox.sourceware.org/libc-alpha/b6a96687-9a4b-414f-849a-45a305898274@redhat.com/
It does this strange claim:
---

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

---

To address that (and a few other valid problems)
I clarified in my spec that the mmap'ed buffer
should not be changed before passing to dlmem().
Then there came this e-mail from Jonathon:

https://inbox.sourceware.org/libc-alpha/630fa17528c6050d60f524aa88ad5a057cae1603.camel@rice.edu/

---

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

---

To address that (obviously false) claim, I posted
this proof:
https://inbox.sourceware.org/libc-alpha/d8c82663-d427-e03c-109c-95f6095ce6d6@yandex.ru/
and later this proof:
https://inbox.sourceware.org/libc-alpha/3f636504-818d-6520-6cf3-484503a8703c@yandex.ru/

After that, I explicitly asked Carlos many
times, both privately and on-list, whether
my proofs were sufficient, or should I
disprove more false claims that directly
contradict with my spec draft, to start with:
https://inbox.sourceware.org/libc-alpha/b2336861-9a01-c5ab-0d78-99ea3f920824@yandex.ru/

Carlos never replied to either of these
queries. And when Szabolcs started to
actually discuss my proposal (rather some
false claims around it), Adhemerval provoked
the conflict (by repeating the aforementioned
disproven claims indefinitely) with the only
goal of making Szabolcs to stop reviewing my
proposal! Fortunately that was unneeded at
that stage, as I was already set back to
scratches by an existing Szabolcs's review.


>   if I understood correctly -- and then he was trying to warn you
> that any *alternative* patch you might come up with would *also* be
> unacceptable if it required the caller to parse the DLL themselves.

As shown above, that applied to my existing
proposal, not to some future one:
https://inbox.sourceware.org/libc-alpha/b6a96687-9a4b-414f-849a-45a305898274@redhat.com/
This mail is very clear on what proposal is
concerned. And there was never a reason
to expect that any future proposal will include
that  problem.

Note that all proves I was posting, were just
silently ignored and the false statement were
repeated indefinitely:
https://inbox.sourceware.org/libc-alpha/a58c0c8c-4117-1a94-7285-40ba18555548@yandex.ru/
This mail is just one of many, many examples
where I asked to stop blindly repeating and
look at least at what proofs I provide. Eventually
it was summarized by Rich Felker this way:
https://inbox.sourceware.org/libc-alpha/20230331145803.GB3298@brightrain.aerifal.cx/

So as you can see, as surprising as it might
seem, it all was just a big campaign on punishing
me for not dropping the patches down. :(

> Do you understand the difference between what you thought Adhemerval was
> saying, and what I am saying he was trying to communicate?  Please
> confirm, or else describe why you don't see a difference so I can try to
> clarify again.

You can only try to clarify that, if you read
the whole thread. Its not possible to clarify
out-of-context. And once you read the whole
thread, you'll have a major difficulties in
clarifying what was done here. :(
I have never had to run through such a level
of abuse in any free-software community.
But it all doesn't matter.
What matters is to come up with the good
proposal, which is what I am working on now
and then, with a hope other people can became
more tolerant to a newcomers that only wanted
to contribute.

> Now. Another thing you must understand before you send in any further
> revisions of the patch is that glibc is extremely reluctant to add new
> APIs.  Witness how much we resisted adding strlcpy, a single function
> with a clear specification and no interdependencies with any other
> part of the C library.  This is why you are getting what probably
> seems to you like an absurd level of negative feedback.  We aren't
> trying to deny that you have a legitimate need, and we aren't saying
> you haven't managed to come up with a proof of concept implementation
> of how that need could be solved -- and yet we ARE saying, no, still
> not good enough.

The explanation of "not good enough" should
also be provided. Well, it was done by Szabolcs,
and so I am on the drawing boards and scratch
pads again.
Or alternatively glibc can add hooks needed
for an external libdl, and not accept any new
libdl-related API after that.
Or alternatively it should be possible to link
with the custom-patched static libc, and put
the resulting binary into a separate namespace.
I was working also in that direction and had
some progress, but I need to understand the
glibc's tls impl better. Overall that route seems
possible too.

Now to your proposal:

> ---
>
> Having said all that, I can imagine other uses for dlmem() or something like it, besides your specific program, so let me try to meet you in the middle here.  Suppose we gave you *this* API:
>
> /** Report the total amount of virtual address space required in order to load
>   *  the shared object referred to by FD.  FD must be a readable and seekable
>   *  file descriptor.  Returns a whole number of pages, or zero on error
>   *  (in which case errno will provide details).
>   */
> size_t dl_total_vsize(int fd);
>
> /** Load the shared object referred to by FD into this process's address
>   *  space.  FD must be a readable and seekable file descriptor.
>   *
>   *  If LOAD_ADDR is not NULL, it specifies the address at which to load
>   *  the shared object.  If this is not possible, for instance if there
>   *  are already memory mappings in the range from LOAD_ADDR to LOAD_ADDR
>   *  plus dl_total_vsize(FD), the operation will fail.  LOAD_ADDR must be
>   *  a multiple of the system page size.
>   *
>   *  MODE accepts the same set of RTLD_* flags as are documented for dlopen(),
>   *  plus one more: RTLD_READ directs the dynamic loader to read the shared
>   *  object into the process's address space as-if with read() rather than
>   *  with mmap().  When this flag is used, LOAD_ADDR may not be NULL, and must
>   *  already point to at least dl_total_vsize(FD) bytes of writable memory.
>   *  The dynamic loader will take ownership of that range of addresses and
>   *  may subsequently (e.g. upon a call to dlclose()) deallocate them as-if
>   *  with munmap().  Furthermore, the dynamic loader will change virtual
>   *  memory protection settings for sub-ranges of this space, so that
>   *  (for instance) the text segment of the shared object is read-only,
>   *  as-if by calling mprotect().
>   */
> void *dl_fdopen(int fd, int mode, void *load_addr);
>
> What would still be missing for your use case?
What's missing here is an ability to load the
library into an existing mapping without overwriting
it. This feature is needed so that the library can
be duplicated from "reloc_addr" to
"reloc_addr+VM_window_start".
So your proposal only adds 1 feature from the
needed 2.

The proposal I am working on now, looks like this:
RTLD_NORELOCATE flag defers an object relocation
upon the use of dlsym(), or an explicitly called
new dlrelocate() fn.
RTLD_DI_MAPINFO is a new dlinfo() cmd that
fills in this struct:

+typedef struct
+{
+  void *map_start;             /* Beginning of mapping containing 
address.  */
+  size_t map_length;           /* Length of mapping.  */
+  int relocated;               /* Indicates whether an object was 
relocated. */
+} Dl_mapinfo;

After getting this struct, the user can
just memcpy() the unrelocated image
where he wants it to be, and then call
dlset_object_address(handle, address);
to update the link-map.
Then he can either call dlrelocate()
or not do anything, in which case dlsym()
will relocate.
That seems to be a very flexible and small
extension: as it is up to the user to move
an object, almost no new code needs to be
added to glibc. All patches I now have, are
within the 200lines margin.

Do you know if such extension is acceptable?

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

* Re: proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function)
  2023-05-01 23:11                                               ` Zack Weinberg
  2023-05-02  5:48                                                 ` stsp
@ 2023-05-02  6:24                                                 ` stsp
  1 sibling, 0 replies; 107+ messages in thread
From: stsp @ 2023-05-02  6:24 UTC (permalink / raw)
  To: Zack Weinberg
  Cc: GNU libc development, Adhemerval Zanella, Carlos O'Donell


02.05.2023 04:11, Zack Weinberg пишет:
> Now. Another thing you must understand before you send in any further
> revisions of the patch is that glibc is extremely reluctant to add new
> APIs.  Witness how much we resisted adding strlcpy, a single function
> with a clear specification and no interdependencies with any other
> part of the C library.
I think strlcpy() is a rather bad example
here. I am using strlcpy() from libbsd, I
have no problems with such usage, and
I would never recommend adding it to
glibc, for a very simple reason: its already
in libbsd and works well.

But my problem is different because glibc
is deficient on any libdl extensions: external
libdl extensions are simply not possible
right now. If this is addressed, then I would
never recommend having dlmem() in glibc -
it will be in some other unrelated lib then.
One way is to allow the customly-patched
static libc to reside in a separate NS.
I can work even on that, even though it
requires quite a lot of knowledge on glibc
internals, so I was stuck with tls.
The problem is that I was never told on
what should I work to solve my problem.
The only thing I got, was a "drop your
patches immediately" campaign. That is
the main problem. Technical problems are
solvable, but political ones - not.


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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 21:17         ` Carlos O'Donell
                             ` (3 preceding siblings ...)
  2023-04-14 16:15           ` Autoconf maintenance (extremely tangential to Re: [PATCH v9 0/13] implement dlmem() function) Zack Weinberg
@ 2023-05-08 15:05           ` stsp
  2023-05-19  7:26           ` stsp
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-05-08 15:05 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha, Jonathon Anderson

Hello,

14.04.2023 02:17, Carlos O'Donell пишет:
> In summary:
>
> - This API as designed does not have consensus. Posting v10 does not fix the
>    consensus problem.
>
> - I suggest dropping the feature for glibc. If there are other things you
>    would like to work on in glibc, please reach out again.
Your wish is my command.
dlmem patches are dropped, and the
alternative proposal is here:
https://sourceware.org/pipermail/libc-alpha/2023-May/147899.html
Full implementation and test coverage
is here:
https://sourceware.org/bugzilla/show_bug.cgi?id=30007

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 12:38                               ` Szabolcs Nagy
  2023-04-13 15:59                                 ` stsp
@ 2023-05-08 15:10                                 ` stsp
  1 sibling, 0 replies; 107+ messages in thread
From: stsp @ 2023-05-08 15:10 UTC (permalink / raw)
  To: Szabolcs Nagy, Rich Felker
  Cc: Adhemerval Zanella Netto, libc-alpha, janderson, Carlos O'Donell

Hi Szabolcs, I've finally completed an impl
of RTLD_NORELOCATE:
https://sourceware.org/pipermail/libc-alpha/2023-May/147899.html
It appears to be a much more flexible
approach than dlmem(), so I just managed
to build the entire functionality I need, over
that new proposal, rather than using it only
for a part of functionality, as I thought before.
Implementation with test coverage and full
descriptions, is here:

https://sourceware.org/bugzilla/show_bug.cgi?id=30007

Is this more viable, what do you think?


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

* Re: proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function)
  2023-05-02  5:48                                                 ` stsp
@ 2023-05-08 16:00                                                   ` stsp
  0 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-05-08 16:00 UTC (permalink / raw)
  To: Zack Weinberg
  Cc: GNU libc development, Adhemerval Zanella, Carlos O'Donell


02.05.2023 10:48, stsp пишет:
>
> Hi Zack,
>
> 02.05.2023 04:11, Zack Weinberg пишет:
>> This discussion seems to have died down in the past two weeks but I'm
>> going to make one last attempt to bridge the divide.  I would hate to
>> see everyone walk away from this with a bad taste in their mouth.  Note
>> cc: list aggressively pruned.
>
> The discussion have died down because I
> went implementing the RTLD_NORELOCATE
> proposal - a replacement to the dlmem()'s
> one. Currently its almost completed and I
> will draft its spec at the bottom of this e-mail.
>
And it finally is available for your review:
https://sourceware.org/pipermail/libc-alpha/2023-May/147899.html

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

* Re: [PATCH v9 0/13] implement dlmem() function
  2023-04-13 21:17         ` Carlos O'Donell
                             ` (4 preceding siblings ...)
  2023-05-08 15:05           ` [PATCH v9 0/13] implement dlmem() function stsp
@ 2023-05-19  7:26           ` stsp
  5 siblings, 0 replies; 107+ messages in thread
From: stsp @ 2023-05-19  7:26 UTC (permalink / raw)
  To: Carlos O'Donell, libc-alpha, Jonathon Anderson

Hello,

14.04.2023 02:17, Carlos O'Donell пишет:
>   If there are other things you
>    would like to work on in glibc, please reach out again.
So I posted the entire patch-set to an ML now:

https://sourceware.org/pipermail/libc-alpha/2023-May/148241.html

Patch bot seems to succeed with these:
https://patchwork.sourceware.org/project/glibc/patch/20230518082854.3903342-15-stsp2@yandex.ru/

Is there anything else to do on my side to
reach out again?


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

end of thread, other threads:[~2023-05-19  7:26 UTC | newest]

Thread overview: 107+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-18 16:50 [PATCH v9 0/13] implement dlmem() function Stas Sergeev
2023-03-18 16:50 ` [PATCH 01/13] elf: strdup() l_name if no realname [BZ #30100] Stas Sergeev
2023-03-29 13:54   ` Adhemerval Zanella Netto
2023-03-29 14:12     ` stsp
2023-03-29 14:19       ` Adhemerval Zanella Netto
2023-03-29 14:28         ` stsp
2023-03-29 14:30           ` Adhemerval Zanella Netto
2023-03-29 14:33             ` stsp
2023-03-18 16:50 ` [PATCH 02/13] elf: switch _dl_map_segment() to anonymous mapping Stas Sergeev
2023-03-29 17:01   ` Adhemerval Zanella Netto
2023-03-29 18:00     ` stsp
2023-03-29 18:29       ` Adhemerval Zanella Netto
2023-03-29 18:46         ` stsp
2023-03-29 19:17           ` Adhemerval Zanella Netto
2023-03-29 19:43             ` stsp
2023-03-18 16:51 ` [PATCH 03/13] elf: dont pass fd to _dl_process_pt_xx Stas Sergeev
2023-03-29 17:10   ` Adhemerval Zanella Netto
2023-03-30 16:08     ` stsp
2023-03-30 20:46       ` Adhemerval Zanella Netto
2023-03-31 12:02         ` Szabolcs Nagy
2023-03-31 12:54           ` Adhemerval Zanella Netto
2023-03-31 14:04             ` stsp
2023-03-18 16:51 ` [PATCH 04/13] elf: split _dl_map_object_from_fd() into reusable parts Stas Sergeev
2023-03-18 16:51 ` [PATCH 05/13] elf: split open_verify() " Stas Sergeev
2023-03-18 16:51 ` [PATCH 06/13] elf: load elf hdr fully in open_verify() Stas Sergeev
2023-03-18 16:51 ` [PATCH 07/13] elf: convert pread64 to callback in do_open_verify() Stas Sergeev
2023-03-18 16:51 ` [PATCH 08/13] elf: convert _dl_map_segments's mmap() to a callback Stas Sergeev
2023-03-18 16:51 ` [PATCH 09/13] elf: call _dl_map_segment() via premap callback Stas Sergeev
2023-03-18 16:51 ` [PATCH 10/13] elf: convert _dl_map_object to a callback Stas Sergeev
2023-03-18 16:51 ` [PATCH 11/13] elf: split _dl_check_loaded() from _dl_map_object Stas Sergeev
2023-03-18 16:51 ` [PATCH 12/13] dlfcn,elf: implement dlmem() [BZ #11767] Stas Sergeev
2023-03-29 13:45   ` Carlos O'Donell
2023-03-29 13:51     ` stsp
2023-03-29 14:10       ` Jonathon Anderson
2023-03-29 14:20         ` stsp
2023-03-29 14:31           ` Adhemerval Zanella Netto
2023-03-29 15:01             ` stsp
2023-03-29 14:35           ` Carlos O'Donell
2023-03-29 14:50             ` stsp
2023-03-29 15:20               ` Carlos O'Donell
2023-03-29 15:34                 ` stsp
2023-03-30  8:09         ` stsp
2023-03-18 16:51 ` [PATCH 13/13] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag Stas Sergeev
2023-03-29 12:32 ` [PATCH v9 0/13] implement dlmem() function Adhemerval Zanella Netto
2023-03-29 13:10   ` stsp
2023-03-29 13:18   ` stsp
2023-03-31 12:20     ` Szabolcs Nagy
2023-03-31 13:51       ` stsp
2023-03-31 14:49         ` Rich Felker
2023-03-31 14:56           ` stsp
2023-03-31 14:58             ` Rich Felker
2023-03-31 15:03               ` stsp
2023-03-31 14:44       ` stsp
2023-03-31 15:12       ` stsp
2023-03-31 17:12         ` Szabolcs Nagy
2023-03-31 17:36           ` stsp
2023-04-01  9:28             ` stsp
2023-04-03 10:04             ` Szabolcs Nagy
2023-04-03 10:43               ` stsp
2023-04-03 12:01                 ` Szabolcs Nagy
2023-04-03 13:07                   ` stsp
2023-04-05  7:29                   ` stsp
2023-04-05  8:51                     ` Szabolcs Nagy
2023-04-05  9:26                       ` stsp
2023-04-05  9:31                       ` Florian Weimer
2023-04-12 17:23                       ` stsp
2023-04-12 18:00                         ` stsp
2023-04-12 18:20                           ` Rich Felker
2023-04-12 18:46                             ` stsp
2023-04-12 19:52                               ` Zack Weinberg
2023-04-12 19:07                             ` stsp
2023-04-13 10:01                             ` stsp
2023-04-13 12:38                               ` Szabolcs Nagy
2023-04-13 15:59                                 ` stsp
2023-04-13 18:09                                   ` Adhemerval Zanella Netto
2023-04-13 18:59                                     ` stsp
2023-04-13 19:12                                       ` Adhemerval Zanella Netto
2023-04-13 19:29                                         ` stsp
2023-04-13 20:02                                           ` Adhemerval Zanella Netto
2023-04-13 20:21                                             ` stsp
2023-04-13 20:57                                             ` stsp
2023-04-14  7:07                                             ` stsp
2023-04-14  7:36                                             ` stsp
2023-04-14 11:30                                             ` stsp
2023-04-14 19:04                                             ` proof for dlmem() (Re: [PATCH v9 0/13] implement dlmem() function) stsp
2023-05-01 23:11                                               ` Zack Weinberg
2023-05-02  5:48                                                 ` stsp
2023-05-08 16:00                                                   ` stsp
2023-05-02  6:24                                                 ` stsp
2023-05-08 15:10                                 ` [PATCH v9 0/13] implement dlmem() function stsp
2023-03-31 18:47           ` stsp
2023-03-31 19:00             ` stsp
2023-03-29 13:17 ` Carlos O'Donell
2023-03-29 13:26   ` stsp
2023-03-29 17:03   ` stsp
2023-03-29 18:13     ` Carlos O'Donell
2023-03-29 18:29       ` stsp
2023-03-31 11:04       ` stsp
2023-04-13 21:17         ` Carlos O'Donell
2023-04-13 21:58           ` stsp
2023-04-13 22:08           ` stsp
2023-04-13 22:50           ` stsp
2023-04-14 16:15           ` Autoconf maintenance (extremely tangential to Re: [PATCH v9 0/13] implement dlmem() function) Zack Weinberg
2023-04-14 20:24             ` Carlos O'Donell
2023-04-14 20:40               ` Zack Weinberg
2023-05-08 15:05           ` [PATCH v9 0/13] implement dlmem() function stsp
2023-05-19  7:26           ` stsp

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).