[Libguestfs] [PATCH nbdkit 1/9] server: Add libnbdkit.so.

Richard W.M. Jones
2020-08-18T10:50:05Z

This essentially turns the whole of nbdkit into a library.  There is a
rump nbdkit program left which simply contains a main() function that
forwards to the nbdkit_main() function in the library.

The reason for this is to allow nbdkit to be compiled on Windows,
because Windows does not allow shared libraries to contain any
undefined symbols.  nbdkit previously relied on undefined symbols in
all plugins and filters (eg. nbdkit_parse_int) which get resolved at
run time by the nbdkit program.  In order to make this work on Windows
we need to have a new library (libnbdkit.so on Linux, but probably
called something like LIBNBDKIT.DLL on Windows) which will contain
these symbols, and plugins can then be compiled with -lnbdkit so they
will link with this library, hence no undefined symbols.

Note this change is backwards compatible.  Because the rump nbdkit
binary links with -lnbdkit it still contains and reexports all the
symbols, so plugins and filters which don't explicitly link with
-lnbdkit will still be loadable (on existing platforms).
---
 server/Makefile.am                 | 21 ++++++++------
 server/main.c                      |  2 +-
 server/nbdkit.c                    | 46 ++++++++++++++++++++++++++++++
 server/nbdkit.syms                 |  1 +
 tests/test-nbdkit-backend-debug.sh | 24 ++++++++--------
 wrapper.c                          | 10 +++++--
 6 files changed, 80 insertions(+), 24 deletions(-)

diff --git a/server/Makefile.am b/server/Makefile.am
index 58b22341..8cfa0115 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -34,8 +34,12 @@ include $(top_srcdir)/common-rules.mk
 EXTRA_DIST = nbdkit.syms

 sbin_PROGRAMS = nbdkit
+nbdkit_SOURCES = nbdkit.c
+nbdkit_CFLAGS = $(WARNINGS_CFLAGS)
+nbdkit_LDADD = libnbdkit.la

-nbdkit_SOURCES = \
+lib_LTLIBRARIES = libnbdkit.la
+libnbdkit_la_SOURCES = \
 	backend.c \
 	background.c \
 	captive.c \
@@ -70,10 +74,10 @@ nbdkit_SOURCES = \
 	$(top_srcdir)/include/nbdkit-filter.h \
 	$(NULL)
 if ENABLE_LIBFUZZER
-nbdkit_SOURCES += fuzzer.c
+libnbdkit_la_SOURCES += fuzzer.c
 endif

-nbdkit_CPPFLAGS = \
+libnbdkit_la_CPPFLAGS = \
 	-Dbindir=\"$(bindir)\" \
 	-Dlibdir=\"$(libdir)\" \
 	-Dmandir=\"$(mandir)\" \
@@ -86,21 +90,22 @@ nbdkit_CPPFLAGS = \
 	-I$(top_srcdir)/common/protocol \
 	-I$(top_srcdir)/common/utils \
 	$(NULL)
-nbdkit_CFLAGS = \
+libnbdkit_la_CFLAGS = \
 	$(PTHREAD_CFLAGS) \
 	$(WARNINGS_CFLAGS) \
 	$(GNUTLS_CFLAGS) \
 	$(LIBSELINUX_CFLAGS) \
 	$(VALGRIND_CFLAGS) \
 	$(NULL)
-nbdkit_LDADD = \
+libnbdkit_la_LIBADD = \
 	$(GNUTLS_LIBS) \
 	$(LIBSELINUX_LIBS) \
 	$(DL_LIBS) \
 	$(top_builddir)/common/protocol/libprotocol.la \
 	$(top_builddir)/common/utils/libutils.la \
 	$(NULL)
-nbdkit_LDFLAGS = \
+libnbdkit_la_LDFLAGS = \
+	-shared $(NO_UNDEFINED_ON_WINDOWS) \
 	$(PTHREAD_LIBS) \
 	$(DL_LDFLAGS) \
 	$(NULL)
@@ -109,7 +114,7 @@ if USE_LINKER_SCRIPT_FOR_SERVER
 # adds loads of fuzzer and ASAN-related symbols that are required by
 # the plugins but which our linker script tries to hide.
 if !ENABLE_LIBFUZZER
-nbdkit_LDFLAGS += -Wl,--version-script=$(srcdir)/nbdkit.syms
+libnbdkit_la_LDFLAGS += -Wl,--version-script=$(srcdir)/nbdkit.syms
 endif
 endif

@@ -118,7 +123,7 @@ endif

 BUILT_SOURCES = synopsis.c
 EXTRA_DIST += synopsis.c
-EXTRA_nbdkit_DEPENDENCIES = synopsis.c
+EXTRA_libnbdkit_la_DEPENDENCIES = synopsis.c
 CLEANFILES += synopsis.c
 main.c: synopsis.c
 synopsis.c: $(top_srcdir)/docs/synopsis.txt
diff --git a/server/main.c b/server/main.c
index 17c4c324..bb7f863e 100644
--- a/server/main.c
+++ b/server/main.c
@@ -171,7 +171,7 @@ dump_config (void)
 }

 int
-main (int argc, char *argv[])
+nbdkit_main (int argc, char *argv[])
 {
   int c;
   bool help = false, version = false, dump_plugin = false;
diff --git a/server/nbdkit.c b/server/nbdkit.c
new file mode 100644
index 00000000..e2e55cbb
--- /dev/null
+++ b/server/nbdkit.c
@@ -0,0 +1,46 @@
+/* nbdkit
+ * Copyright (C) 2013-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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+
+extern int nbdkit_main (int argc, char *argv[]);
+
+int
+main (int argc, char *argv[])
+{
+  /* This does nothing except call into the real main function in
+   * libnbdkit.so.
+   */
+  return nbdkit_main (argc, argv);
+}
diff --git a/server/nbdkit.syms b/server/nbdkit.syms
index a67669b7..a516cc0f 100644
--- a/server/nbdkit.syms
+++ b/server/nbdkit.syms
@@ -54,6 +54,7 @@
     nbdkit_extents_new;
     nbdkit_get_extent;
     nbdkit_is_tls;
+    nbdkit_main;
     nbdkit_nanosleep;
     nbdkit_parse_bool;
     nbdkit_parse_int8_t;
diff --git a/tests/test-nbdkit-backend-debug.sh
b/tests/test-nbdkit-backend-debug.sh
index 3a28b756..1533ba44 100755
--- a/tests/test-nbdkit-backend-debug.sh
+++ b/tests/test-nbdkit-backend-debug.sh
@@ -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
diff --git a/wrapper.c b/wrapper.c
index c27afae0..7029c2cd 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -168,12 +168,16 @@ main (int argc, char *argv[])
     }
   }

-  /* Needed for plugins written in OCaml. */
+  /* As well as being required by nbdkit, this is also needed for
+   * plugins written in OCaml.
+   */
   s = getenv ("LD_LIBRARY_PATH");
   if (s)
-    r = asprintf (&s, "%s/plugins/ocaml/.libs:%s", builddir, s);
+    r = asprintf (&s,
"%s/server/.libs:%s/plugins/ocaml/.libs:%s",
+                  builddir, builddir, s);
   else
-    r = asprintf (&s, "%s/plugins/ocaml/.libs", builddir);
+    r = asprintf (&s, "%s/server/.libs:%s/plugins/ocaml/.libs",
+                  builddir, builddir);
   if (r < 0) {
     perror ("asprintf");
     exit (EXIT_FAILURE);
--
2.27.0