[Libguestfs] [nbdkit PATCH v4 1/4] server: Export nbdkit_set_dlopen_prefix function

Eric Blake eblake at redhat.com
Sun Feb 16 04:22:10 UTC 2020


At least the VDDK plugin needs a way to load .so files with a custom
directory prepended.  Asking the user to set LD_LIBRARY_PATH is too
strong (that would leak into the child process created by --run, and
makes the user think about issues that we'd rather cover
automatically).  But overriding dlopen() to make it easier to prepend
a directory name under our control requires either a separate
namespace via dlmopen() (which itself is annoying - it messes up gdb
debugging), or that the override occur prior to any other library that
also depends on -ldl.  Which means that if we are going to provide
this functionality, we have to do so from nbdkit proper.

Note that properly injecting a dlopen shim for all subsequent shared
library loads requires that the override itself be in a shared
library.  Thus, nbdkit has to pull it in via a shared library link,
rather than merely a .o file, and we now have to install something in
pkglibdir.  What's more, now that we link against a shared library
instead of just libtool convenience libraries, libtool now creates a
wrapper app named lt-nbdkit so that it can run an in-tree build that
still locates the uninstalled shared library; this affects some
expected output in tests.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 docs/nbdkit-plugin.pod             | 14 +++++
 include/nbdkit-common.h            |  2 +
 server/Makefile.am                 | 13 ++++
 server/nbdkit.syms                 |  1 +
 server/shim.c                      | 99 ++++++++++++++++++++++++++++++
 tests/test-nbdkit-backend-debug.sh | 26 ++++----
 6 files changed, 142 insertions(+), 13 deletions(-)
 create mode 100644 server/shim.c

diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 41bffb7f..f3825067 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -1207,6 +1207,20 @@ and returns C<NULL>.

 The returned string must be freed by the caller.

+=head2 C<nbdkit_set_dlopen_prefix>
+
+ int nbdkit_set_dlopen_prefix (const char *prefix);
+
+Some plugins load a shared library that in turn uses L<dlopen(3)> to
+load further libraries.  If these libraries reside in non-standard
+locations, it may be necessary to prefix any bare library names with
+the desired directory to load them from.  Rather than requiring a user
+to amend C<LD_LIBRARY_PATH> in the calling environment (which would
+have knock-on effects to nbdkit and any child process spawned by the
+B<--run> argument), the plugin can request that nbdkit rewrites all
+dlopen requests that lack a slash character to instead attempt a
+dlopen of a file residing in C<prefix>.
+
 =head2 umask

 All plugins will see a L<umask(2)> of C<0022>.
diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index 50f3dd4f..44abce5e 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -83,6 +83,8 @@ extern void nbdkit_debug (const char *msg, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
 extern void nbdkit_vdebug (const char *msg, va_list args)
   ATTRIBUTE_FORMAT_PRINTF (1, 0);

+extern int nbdkit_set_dlopen_prefix (const char *newdir);
+
 extern char *nbdkit_absolute_path (const char *path);
 extern int64_t nbdkit_parse_size (const char *str);
 extern int nbdkit_parse_bool (const char *str);
diff --git a/server/Makefile.am b/server/Makefile.am
index 9351fefc..b6728511 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -33,6 +33,7 @@ include $(top_srcdir)/common-rules.mk

 EXTRA_DIST = nbdkit.syms

+pkglib_LTLIBRARIES = nbdkit-shim-dlopen.la
 sbin_PROGRAMS = nbdkit

 nbdkit_SOURCES = \
@@ -71,6 +72,17 @@ if ENABLE_LIBFUZZER
 nbdkit_SOURCES += fuzzer.c
 endif

+nbdkit_shim_dlopen_la_SOURCES = shim.c
+nbdkit_shim_dlopen_la_CPPFLAGS = \
+	-I$(top_srcdir)/common/utils \
+	$(NULL)
+nbdkit_shim_dlopen_la_CFLAGS = $(WARNINGS_CFLAGS)
+nbdkit_shim_dlopen_la_LIBADD = $(DL_LIBS)
+nbdkit_shim_dlopen_la_LDFLAGS = \
+	-module -no-undefined -shared -avoid-version \
+	$(DL_LDFLAGS) \
+	$(NULL)
+
 nbdkit_CPPFLAGS = \
 	-Dbindir=\"$(bindir)\" \
 	-Dlibdir=\"$(libdir)\" \
@@ -92,6 +104,7 @@ nbdkit_CFLAGS = \
 	$(VALGRIND_CFLAGS) \
 	$(NULL)
 nbdkit_LDADD = \
+	nbdkit-shim-dlopen.la \
 	$(GNUTLS_LIBS) \
 	$(LIBSELINUX_LIBS) \
 	$(DL_LIBS) \
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index 96c22c07..d20e0784 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -63,6 +63,7 @@
     nbdkit_peer_name;
     nbdkit_read_password;
     nbdkit_realpath;
+    nbdkit_set_dlopen_prefix;
     nbdkit_set_error;
     nbdkit_vdebug;
     nbdkit_verror;
diff --git a/server/shim.c b/server/shim.c
new file mode 100644
index 00000000..a2682820
--- /dev/null
+++ b/server/shim.c
@@ -0,0 +1,99 @@
+/* nbdkit
+ * Copyright (C) 2020 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* This file provides a shim around dlopen, to aid plugins such as
+ * vddk that want to convert relative shared library requests from
+ * subsequent code into absolute requests.  This library has to be a
+ * load-time dependency of the main nbdkit executable prior to the
+ * library providing the real dlopen (libdl on Linux, libc on BSD).
+ */
+
+#include <config.h>
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal.h"
+
+static void *(*orig_dlopen) (const char *filename, int flags);
+
+/* NULL for normal behavior, or set when we want to override
+ * relative dlopen()s to instead be an absolute open with prefix.
+ */
+static char *dir;
+
+int
+nbdkit_set_dlopen_prefix (const char *newdir)
+{
+  free (dir);
+  if (newdir) {
+    dir = strdup (newdir);
+    if (!dir) {
+      nbdkit_error ("strdup: %m");
+      return -1;
+    }
+  }
+  else
+    dir = NULL;
+  return 0;
+}
+
+void *
+dlopen (const char *filename, int flags)
+{
+  /* Using CLEANUP_FREE would make this shared library bigger. */
+  char *tmp = NULL;
+  void *ret;
+
+  if (dir && ! strchr (filename, '/')) {
+    /* Caller expects failure to set dlerror, so we still have to call
+     * dlopen; the best we can do is log our allocation failure.
+     */
+    if (asprintf (&tmp, "%s/%s", dir, filename) >= 0)
+      filename = tmp;
+    else
+      nbdkit_debug ("dlopen: failed to allocate replacement for %s", filename);
+  }
+
+  ret = orig_dlopen (filename, flags);
+  free (tmp);
+  return ret;
+}
+
+static void constructor (void) __attribute__ ((constructor));
+static void
+constructor (void)
+{
+  orig_dlopen = dlsym (RTLD_NEXT, "dlopen");
+}
diff --git a/tests/test-nbdkit-backend-debug.sh b/tests/test-nbdkit-backend-debug.sh
index 3a28b756..8fe78b48 100755
--- a/tests/test-nbdkit-backend-debug.sh
+++ b/tests/test-nbdkit-backend-debug.sh
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 # nbdkit
-# Copyright (C) 2019 Red Hat Inc.
+# Copyright (C) 2019-2020 Red Hat Inc.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -49,10 +49,10 @@ nbdkit -U - \
        --run "qemu-img convert \$nbd $out" |& tee $debug

 # Should contain all debugging messages.
-grep '^nbdkit:.*debug: nofilter: open' $debug
-grep '^nbdkit:.*debug: memory: open' $debug
-grep '^nbdkit:.*debug: nofilter: pread' $debug
-grep '^nbdkit:.*debug: memory: pread' $debug
+grep 'nbdkit:.*debug: nofilter: open' $debug
+grep 'nbdkit:.*debug: memory: open' $debug
+grep 'nbdkit:.*debug: nofilter: pread' $debug
+grep 'nbdkit:.*debug: memory: pread' $debug

 nbdkit -U - \
        -v -D nbdkit.backend.controlpath=0 \
@@ -61,10 +61,10 @@ nbdkit -U - \
        --run "qemu-img convert \$nbd $out" |& tee $debug

 # Should contain only datapath messages.
-grep -v '^nbdkit:.*debug: nofilter: open' $debug
-grep -v '^nbdkit:.*debug: memory: open' $debug
-grep '^nbdkit:.*debug: nofilter: pread' $debug
-grep '^nbdkit:.*debug: memory: pread' $debug
+grep -v 'nbdkit:.*debug: nofilter: open' $debug
+grep -v 'nbdkit:.*debug: memory: open' $debug
+grep 'nbdkit:.*debug: nofilter: pread' $debug
+grep 'nbdkit:.*debug: memory: pread' $debug

 nbdkit -U - \
        -v -D nbdkit.backend.datapath=0 \
@@ -73,7 +73,7 @@ nbdkit -U - \
        --run "qemu-img convert \$nbd $out" |& tee $debug

 # Should contain only controlpath messages.
-grep '^nbdkit:.*debug: nofilter: open' $debug
-grep '^nbdkit:.*debug: memory: open' $debug
-grep -v '^nbdkit:.*debug: nofilter: pread' $debug
-grep -v '^nbdkit:.*debug: memory: pread' $debug
+grep 'nbdkit:.*debug: nofilter: open' $debug
+grep 'nbdkit:.*debug: memory: open' $debug
+grep -v 'nbdkit:.*debug: nofilter: pread' $debug
+grep -v 'nbdkit:.*debug: memory: pread' $debug
-- 
2.24.1




More information about the Libguestfs mailing list