[2/6] criu: Use shared libraries for optional functionality

Submitted by Dmitry Safonov on Nov. 23, 2019, 11:46 p.m.

Details

Message ID 20191123234627.483372-3-dima@arista.com
State New
Series "criu: dlopen() optional libraries"
Headers show

Commit Message

Dmitry Safonov Nov. 23, 2019, 11:46 p.m.
Introduce cr-libs for using .so libraries those not critical for CRIU
functioning, but provide extended functionality.

Signed-off-by: Dmitry Safonov <dima@arista.com>
---
 criu/Makefile.crtools  |   1 +
 criu/cr-libs.c         | 132 +++++++++++++++++++++++++++++++++++++++++
 criu/crtools.c         |  18 +++++-
 criu/include/cr-libs.h |  38 ++++++++++++
 4 files changed, 186 insertions(+), 3 deletions(-)
 create mode 100644 criu/cr-libs.c
 create mode 100644 criu/include/cr-libs.h

Patch hide | download patch | download mbox

diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools
index d19ff8123b95..1833ba77526d 100644
--- a/criu/Makefile.crtools
+++ b/criu/Makefile.crtools
@@ -14,6 +14,7 @@  obj-y			+= cr-check.o
 obj-y			+= cr-dedup.o
 obj-y			+= cr-dump.o
 obj-y			+= cr-errno.o
+obj-y			+= cr-libs.o
 obj-y			+= cr-restore.o
 obj-y			+= cr-service.o
 obj-y			+= crtools.o
diff --git a/criu/cr-libs.c b/criu/cr-libs.c
new file mode 100644
index 000000000000..b7da1f847afa
--- /dev/null
+++ b/criu/cr-libs.c
@@ -0,0 +1,132 @@ 
+#include <dlfcn.h>
+#include <link.h>
+#include <linux/limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "common/compiler.h"
+#include "cr-libs.h"
+#include "log.h"
+
+struct so_desc {
+	const char	*so_name;
+	const char	*warning;
+
+	/*
+	 * You have to specify major version to make sure that ABI
+	 * version of library is the required.
+	 */
+	const unsigned	major;
+	/* other major versions supported */
+	const unsigned	*also_supported;
+	const size_t	also_supported_sz;
+
+	/* Set up on load */
+	void		*so_handle;
+	unsigned	loaded_major;
+};
+static struct so_desc crlibs[SHARED_LIB_LAST] = {
+};
+
+static void *try_load_name_version(const char *so_name, const unsigned major)
+{
+	char buf[PATH_MAX];
+	void *ret;
+
+	snprintf(buf, ARRAY_SIZE(buf), "%s.%u", so_name, major);
+
+	ret = dlopen(buf, RTLD_LAZY);
+	if (ret)
+		pr_debug("Loaded `%s' succesfully\n", buf);
+
+	return ret;
+}
+
+static void try_load_lib(struct so_desc *lib)
+{
+	size_t i;
+
+	lib->so_handle = try_load_name_version(lib->so_name, lib->major);
+	if (lib->so_handle) {
+		lib->loaded_major = lib->major;
+		return;
+	}
+
+	for (i = 0; i < lib->also_supported_sz; i++) {
+		unsigned ver = lib->also_supported[i];
+
+		lib->so_handle = try_load_name_version(lib->so_name, ver);
+		if (lib->so_handle) {
+			lib->loaded_major = ver;
+			return;
+		}
+	}
+
+	print_once(LOG_INFO, "CRIU functionality may be limited\n");
+	pr_info("Can't load a shared library %s: %s\n", lib->so_name, dlerror());
+	if (lib->warning)
+		pr_info("%s: %s\n", lib->so_name, lib->warning);
+}
+
+void shared_libs_load(void)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(crlibs); i++)
+		try_load_lib(&crlibs[i]);
+}
+
+void shared_libs_unload(void)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(crlibs); i++) {
+		struct so_desc *lib = &crlibs[i];
+
+		if (lib->so_handle == NULL)
+			continue;
+
+		if (dlclose(lib->so_handle))
+			pr_warn("Failed to unload `%s': %s\n",
+				lib->so_name, dlerror());
+		lib->so_handle = NULL;
+	}
+}
+
+void *shared_libs_lookup(enum shared_libs id, const char *func)
+{
+	struct so_desc *lib;
+	void *ret;
+
+	if (id > SHARED_LIB_LAST) {
+		pr_err("BUG: shared library id is too big: %u\n", id);
+		return NULL;
+	}
+
+	lib = &crlibs[id];
+	if (lib->so_handle == NULL)
+		return NULL;
+
+	ret = dlsym(lib->so_handle, func);
+	if (ret == NULL)
+		pr_debug("Can't find `%s' function in %s\n", func, lib->so_name);
+
+	return ret;
+}
+
+int shared_libs_major(enum shared_libs id, unsigned *major)
+{
+	struct so_desc *lib;
+
+	if (id > SHARED_LIB_LAST) {
+		pr_err("BUG: shared library id is too big: %u\n", id);
+		return -1;
+	}
+
+	lib = &crlibs[id];
+	if (lib->so_handle == NULL)
+		return -1;
+
+	*major = lib->loaded_major;
+	return 0;
+}
diff --git a/criu/crtools.c b/criu/crtools.c
index 1bf2d98c35b0..25cce4265cce 100644
--- a/criu/crtools.c
+++ b/criu/crtools.c
@@ -25,6 +25,7 @@ 
 #include "common/compiler.h"
 #include "crtools.h"
 #include "cr_options.h"
+#include "cr-libs.h"
 #include "external.h"
 #include "files.h"
 #include "sk-inet.h"
@@ -48,13 +49,24 @@ 
 #include "sysctl.h"
 #include "img-remote.h"
 
-void flush_early_log_to_stderr() __attribute__((destructor));
-
-void flush_early_log_to_stderr(void)
+__attribute__((destructor))
+static void flush_early_log_to_stderr()
 {
 	flush_early_log_buffer(STDERR_FILENO);
 }
 
+__attribute__((destructor))
+static void exit_shared_libs(void)
+{
+	shared_libs_unload();
+}
+
+__attribute__((constructor))
+static void init_shared_libs(void)
+{
+	shared_libs_load();
+}
+
 int main(int argc, char *argv[], char *envp[])
 {
 	int ret = -1;
diff --git a/criu/include/cr-libs.h b/criu/include/cr-libs.h
new file mode 100644
index 000000000000..ef6ceca1f64d
--- /dev/null
+++ b/criu/include/cr-libs.h
@@ -0,0 +1,38 @@ 
+#ifndef __CR_LIBS_H__
+#define __CR_LIBS_H__
+
+#ifdef CR_NOGLIBC
+#error Shared libraries are not supported for PIEs
+#endif
+
+enum shared_libs {
+	SHARED_LIB_LAST = 0,
+};
+
+/* Called on init to dlopen() all .so libraries */
+extern void shared_libs_load(void);
+
+/* Called on exit to call all libraries destructors */
+extern void shared_libs_unload(void);
+
+/* Loaded library's major version number */
+extern int shared_libs_major(enum shared_libs id, unsigned *major);
+
+/*
+ * Lookup a function in a shared library.
+ * Returns NULL on failure.
+ */
+extern void *shared_libs_lookup(enum shared_libs id, const char *func);
+
+#define shared_libs_lookup_once(id, func)			\
+({								\
+	static int searched;					\
+	static void *ret;					\
+	if (!searched) {					\
+		ret = shared_libs_lookup(id, func);		\
+		searched = 1;					\
+	}							\
+	ret;							\
+})
+
+#endif /* __CR_LIBS_H__ */