public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Stas Sergeev <stsp2@yandex.ru>
To: libc-alpha@sourceware.org
Cc: Stas Sergeev <stsp2@yandex.ru>
Subject: [PATCH 11/12] dlfcn,elf: impl DLMEM_DONTREPLACE dlmem() flag
Date: Mon,  3 Apr 2023 14:04:20 +0500	[thread overview]
Message-ID: <20230403090421.560208-12-stsp2@yandex.ru> (raw)
In-Reply-To: <20230403090421.560208-1-stsp2@yandex.ru>

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

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

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

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 dlfcn/Makefile        |   5 +-
 dlfcn/dlfcn.h         |   4 +
 dlfcn/glreflib1.c     |   2 +
 dlfcn/tst-dlmem-shm.c | 169 ++++++++++++++++++++++++++++++++++++++++++
 elf/dl-load.c         |  36 ++++++++-
 elf/dl-map-segments.h |   4 +-
 6 files changed, 216 insertions(+), 4 deletions(-)
 create mode 100644 dlfcn/tst-dlmem-shm.c

diff --git a/dlfcn/Makefile b/dlfcn/Makefile
index 55e5f5fcdf..a71810e941 100644
--- a/dlfcn/Makefile
+++ b/dlfcn/Makefile
@@ -52,8 +52,10 @@ endif
 ifeq (yes,$(build-shared))
 tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
 	bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
-	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem-extfns
+	bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem-extfns \
+	tst-dlmem-shm
 CPPFLAGS-tst-dlmem-extfns.c += -DBUILDDIR=\"$(objpfx)\"
+CPPFLAGS-tst-dlmem-shm.c += -DBUILDDIR=\"$(objpfx)\"
 endif
 modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
 		defaultmod2 errmsg1mod modatexit modcxaatexit \
@@ -110,6 +112,7 @@ $(objpfx)glreflib1.img: $(objpfx)glreflib1.so
 	cat $^ >>$@
 	dd if=/dev/urandom bs=512 count=1 >>$@
 $(objpfx)tst-dlmem-extfns.out: $(objpfx)glreflib1.so $(objpfx)glreflib1.img
+$(objpfx)tst-dlmem-shm.out: $(objpfx)glreflib1.so
 
 $(objpfx)tst-dlinfo.out: $(objpfx)glreflib3.so
 LDFLAGS-glreflib3.so = -Wl,-rpath,:
diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h
index fe5e5a7d09..7aa9d7d3cf 100644
--- a/dlfcn/dlfcn.h
+++ b/dlfcn/dlfcn.h
@@ -73,6 +73,10 @@ typedef void *
 (dlmem_premap_t) (void *mappref, size_t maplength, size_t mapalign,
 	          void *cookie);
 
+/* Do not replace mapping created by premap callback.
+   dlmem() will then use memcpy(). */
+#define DLMEM_DONTREPLACE 1
+
 struct dlmem_args {
   /* Optional name to associate with the loaded object. */
   const char *soname;
diff --git a/dlfcn/glreflib1.c b/dlfcn/glreflib1.c
index f26832fabe..bab3fcd1b0 100644
--- a/dlfcn/glreflib1.c
+++ b/dlfcn/glreflib1.c
@@ -22,3 +22,5 @@ ref1 (void)
 {
   return 42;
 }
+
+int bar = 35;
diff --git a/dlfcn/tst-dlmem-shm.c b/dlfcn/tst-dlmem-shm.c
new file mode 100644
index 0000000000..7899dfc909
--- /dev/null
+++ b/dlfcn/tst-dlmem-shm.c
@@ -0,0 +1,169 @@
+/* Test for dlmem into shm.
+   Copyright (C) 2000-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <dlfcn.h>
+#include <link.h>
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <support/check.h>
+
+static size_t maplen;
+
+static void *
+premap_dlmem (void *mappref, size_t maplength, size_t mapalign, void *cookie)
+{
+  int fd = * (int *) cookie;
+  int prot = PROT_READ | PROT_WRITE;
+  int err;
+
+  /* See if we support such parameters. */
+  if (mappref || mapalign > 4096)
+    return MAP_FAILED;
+
+  fprintf (stderr, "%s\n", __func__);
+
+  err = ftruncate (fd, maplength);
+  if (err)
+    error (EXIT_FAILURE, 0, "ftruncate() failed");
+  maplen = maplength;
+  return mmap (NULL, maplength, prot, MAP_SHARED | MAP_FILE
+#ifdef MAP_32BIT
+                                      | MAP_32BIT
+#endif
+                                      , fd, 0);
+}
+
+#define TEST_FUNCTION do_test
+extern int do_test (void);
+
+int
+do_test (void)
+{
+  void *handle;
+  void *addr;
+  int (*sym) (void); /* We load ref1 from glreflib1.c.  */
+  int *bar, *bar2;
+  unsigned char *addr2;
+  Dl_info info;
+  int ret;
+  int fd;
+  int num;
+  off_t len;
+  struct link_map *lm;
+  const char *shm_name = "/tst-dlmem";
+  int shm_fd;
+  struct dlmem_args a;
+
+  shm_fd = memfd_create (shm_name, 0);
+  if (shm_fd == -1)
+    error (EXIT_FAILURE, 0, "shm_open() failed");
+
+  fd = open (BUILDDIR "glreflib1.so", O_RDONLY);
+  if (fd == -1)
+    error (EXIT_FAILURE, 0, "cannot open: glreflib1.so");
+  len = lseek (fd, 0, SEEK_END);
+  lseek (fd, 0, SEEK_SET);
+  addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+  if (addr == MAP_FAILED)
+    error (EXIT_FAILURE, 0, "cannot mmap: glreflib1.so");
+  a.soname = "glreflib1.so";
+  a.flags = DLMEM_DONTREPLACE;
+  a.nsid = LM_ID_BASE;
+  a.premap = premap_dlmem;
+  a.cookie = &shm_fd;
+  handle = dlmem (addr, len, RTLD_NOW | RTLD_LOCAL, &a);
+  if (handle == NULL)
+    error (EXIT_FAILURE, 0, "cannot load: glreflib1.so");
+  munmap (addr, len);
+  close (fd);
+  /* Check if premap was called. */
+  TEST_VERIFY (maplen != 0);
+
+  sym = dlsym (handle, "ref1");
+  if (sym == NULL)
+    error (EXIT_FAILURE, 0, "dlsym failed");
+
+  memset (&info, 0, sizeof (info));
+  ret = dladdr (sym, &info);
+  if (ret == 0)
+    error (EXIT_FAILURE, 0, "dladdr failed");
+#ifdef MAP_32BIT
+  /* Make sure MAP_32BIT worked. */
+  if ((unsigned long) info.dli_fbase >= 0x100000000)
+    error (EXIT_FAILURE, 0, "premap audit didn't work");
+#endif
+  ret = dlinfo (handle, RTLD_DI_LINKMAP, &lm);
+  if (ret != 0)
+    error (EXIT_FAILURE, 0, "dlinfo failed");
+
+  printf ("info.dli_fname = %p (\"%s\")\n", info.dli_fname, info.dli_fname);
+  printf ("info.dli_fbase = %p\n", info.dli_fbase);
+  printf ("info.dli_sname = %p (\"%s\")\n", info.dli_sname, info.dli_sname);
+  printf ("info.dli_saddr = %p\n", info.dli_saddr);
+  printf ("lm->l_addr = %lx\n", lm->l_addr);
+
+  if (info.dli_fname == NULL)
+    error (EXIT_FAILURE, 0, "dli_fname is NULL");
+  if (info.dli_fbase == NULL)
+    error (EXIT_FAILURE, 0, "dli_fbase is NULL");
+  if (info.dli_sname == NULL)
+    error (EXIT_FAILURE, 0, "dli_sname is NULL");
+  if (info.dli_saddr == NULL)
+    error (EXIT_FAILURE, 0, "dli_saddr is NULL");
+
+  num = sym ();
+  if (num != 42)
+    error (EXIT_FAILURE, 0, "bad return from ref1");
+
+  /* Now try symbol duplication. */
+  bar = dlsym (handle, "bar");
+  if (bar == NULL)
+    error (EXIT_FAILURE, 0, "dlsym failed");
+  TEST_COMPARE (*bar, 35);
+  /* write another value */
+#define TEST_BAR_VAL 48
+  *bar = TEST_BAR_VAL;
+
+  /* Create second instance of the solib. */
+  addr2 = mmap (NULL, maplen, PROT_READ | PROT_WRITE | PROT_EXEC,
+               MAP_SHARED, shm_fd, 0);
+  if (addr2 == MAP_FAILED)
+    error (EXIT_FAILURE, 0, "cannot mmap shm\n");
+  /* Find our bar symbol duplicate. */
+  ret = dladdr (bar, &info);
+  if (ret == 0)
+    error (EXIT_FAILURE, 0, "dladdr failed");
+  bar2 = (int *) (addr2 + (info.dli_saddr - info.dli_fbase));
+  /* See if we found the right one. */
+  TEST_COMPARE (*bar2, TEST_BAR_VAL);
+
+  munmap (addr2, maplen);
+  close (shm_fd);
+  dlclose (handle);
+
+  return 0;
+}
+
+
+#include <support/test-driver.c>
diff --git a/elf/dl-load.c b/elf/dl-load.c
index fd81a9103e..422c03459b 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2352,6 +2352,31 @@ _dl_map_object (struct link_map *loader, const char *name,
   return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid);
 }
 
+static void *
+do_mmapcpy (void *addr, size_t length, int prot, int flags,
+            void *arg, off_t offset)
+{
+  const struct dlmem_fbuf *fb = arg;
+  size_t to_copy = 0;
+
+  assert (flags & MAP_FIXED);
+  assert ((flags & MAP_ANONYMOUS) || fb);
+
+  if (!(flags & MAP_ANONYMOUS) && offset < fb->len)
+    {
+      to_copy = length;
+      if (offset + to_copy > fb->len)
+        to_copy = fb->len - offset;
+      memcpy (addr, fb->buf + offset, to_copy);
+    }
+  /* memset the rest. */
+  if (length > to_copy)
+    memset (addr + to_copy, 0, length - to_copy);
+  if (__mprotect (addr, length, prot) == -1)
+    return MAP_FAILED;
+  return addr;
+}
+
 static void *
 do_memremap (void *addr, size_t length, int prot, int flags,
              void *arg, off_t offset)
@@ -2360,6 +2385,11 @@ do_memremap (void *addr, size_t length, int prot, int flags,
   size_t to_copy = 0;
 
   assert (flags & MAP_FIXED);
+  assert ((flags & MAP_ANONYMOUS) || fb);
+
+  if (flags & MAP_ANONYMOUS)
+    return __mmap (addr, length, prot, flags, -1, 0);
+
   if (offset < fb->len)
     {
       to_copy = length;
@@ -2430,6 +2460,10 @@ ___dl_map_object_from_mem (struct link_map *loader, const char *name,
   struct r_debug *r = _dl_debug_update (nsid);
   bool make_consistent = false;
   struct r_file_id id = {};
+  const struct dlmem_fbuf *fb = private;
+  unsigned dlmem_flags = fb->dlm_args ? fb->dlm_args->flags : 0;
+  __typeof (do_mmap) *m_map = (dlmem_flags & DLMEM_DONTREPLACE)
+                             ? do_mmapcpy : do_memremap;
 
   assert (nsid >= 0);
   assert (nsid < GL(dl_nns));
@@ -2480,7 +2514,7 @@ ___dl_map_object_from_mem (struct link_map *loader, const char *name,
 
   void *stack_end = __libc_stack_end;
   if (_dl_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval,
-                        &errstring, do_memremap, do_dlmem_premap))
+                        &errstring, m_map, do_dlmem_premap))
     goto lose;
 
   _dl_map_object_2 (l, mode, id, NULL, nsid);
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 7e3c3b6d53..3fb9aac6cb 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -187,9 +187,9 @@ _dl_map_segments (struct link_map *l, void *fd,
             {
               /* Map the remaining zero pages in from the zero fill FD.  */
               caddr_t mapat;
-              mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
+              mapat = m_map ((caddr_t) zeropage, zeroend - zeropage,
                               c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
-                              -1, 0);
+                              NULL, 0);
               if (__glibc_unlikely (mapat == MAP_FAILED))
                 return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL;
             }
-- 
2.37.2


  parent reply	other threads:[~2023-04-03  9:04 UTC|newest]

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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230403090421.560208-12-stsp2@yandex.ru \
    --to=stsp2@yandex.ru \
    --cc=libc-alpha@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).