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 <[email protected]> --- 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 _______________________________________________ Libguestfs mailing list [email protected] https://www.redhat.com/mailman/listinfo/libguestfs
