]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
lib: Introduce libukbinfmt
authorMichalis Pappas <michalis@unikraft.io>
Wed, 10 Apr 2024 09:55:20 +0000 (11:55 +0200)
committerUnikraft Bot <monkey@unikraft.io>
Fri, 17 Jan 2025 14:59:21 +0000 (14:59 +0000)
libukbinfmt provides a minimal framework to register handlers of
executable files. Typical examples include binary executables like
ELF objects, or interpreted files like *nix scripts that use the
sha-bang sequence to specify an interpreter.

This commit only implements the functionality required to register
and execute loaders within the kernel's scope. Additional
functionality incl. application support via Linux's `binfmt_misc`
API shall be added as a future extension.

Checkpatch-Ignore: COMPLEX_MACRO
Checkpatch-Ignore: MACRO_ARG_REUSE

Signed-off-by: Michalis Pappas <michalis@unikraft.io>
Approved-by: Andrei Tatar <andrei@unikraft.io>
Reviewed-by: Sergiu Moga <sergiu@unikraft.io>
Reviewed-by: Andrei Tatar <andrei@unikraft.io>
GitHub-Closes: #1386

lib/Makefile.uk
lib/ukbinfmt/Config.uk [new file with mode: 0644]
lib/ukbinfmt/Makefile.uk [new file with mode: 0644]
lib/ukbinfmt/binfmt.c [new file with mode: 0644]
lib/ukbinfmt/include/uk/binfmt.h [new file with mode: 0644]

index d297030fb4aaaa70b427849d31c620a2a0981f71..e2a14e553f2152bb8a5d1562e05d617951589dbe 100644 (file)
@@ -72,3 +72,4 @@ $(eval $(call import_lib,$(CONFIG_UK_BASE)/lib/ukrust))
 $(eval $(call import_lib,$(CONFIG_UK_BASE)/lib/ukreloc))
 $(eval $(call import_lib,$(CONFIG_UK_BASE)/lib/ukofw))
 $(eval $(call import_lib,$(CONFIG_UK_BASE)/lib/ukallocstack))
+$(eval $(call import_lib,$(CONFIG_UK_BASE)/lib/ukbinfmt))
diff --git a/lib/ukbinfmt/Config.uk b/lib/ukbinfmt/Config.uk
new file mode 100644 (file)
index 0000000..8504828
--- /dev/null
@@ -0,0 +1,5 @@
+menuconfig LIBUKBINFMT
+       bool "ukbinfmt: Register loaders for executables"
+       help
+               libukbinfmt provides a minimal framework to register loaders of
+               different types of executable files
diff --git a/lib/ukbinfmt/Makefile.uk b/lib/ukbinfmt/Makefile.uk
new file mode 100644 (file)
index 0000000..2c9baad
--- /dev/null
@@ -0,0 +1,6 @@
+$(eval $(call addlib_s,libukbinfmt,$(CONFIG_LIBUKBINFMT)))
+
+CINCLUDES-$(CONFIG_LIBUKBINFMT)   += -I$(LIBUKBINFMT_BASE)/include
+CXXINCLUDES-$(CONFIG_LIBUKBINFMT) += -I$(LIBUKBINFMT_BASE)/include
+
+LIBUKBINFMT_SRCS-y += $(LIBUKBINFMT_BASE)/binfmt.c
diff --git a/lib/ukbinfmt/binfmt.c b/lib/ukbinfmt/binfmt.c
new file mode 100644 (file)
index 0000000..b5f4aec
--- /dev/null
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2024, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#include <errno.h>
+
+#include <uk/assert.h>
+#include <uk/binfmt.h>
+#include <uk/list.h>
+
+static UK_LIST_HEAD(uk_binfmt_loader_list);
+
+#define uk_binfmt_loader_foreach(_loader, _type)                            \
+       uk_list_for_each_entry((_loader), &uk_binfmt_loader_list, list_head) \
+               if ((_loader)->type == (_type))
+
+int uk_binfmt_register(struct uk_binfmt_loader *loader)
+{
+       UK_ASSERT(loader);
+       UK_ASSERT(loader->name);
+       UK_ASSERT(loader->ops.load);
+       UK_ASSERT(loader->ops.unload);
+       UK_ASSERT(loader->type != UK_BINFMT_LOADER_TYPE_NONE);
+
+       uk_pr_debug("Register loader: %s\n", loader->name);
+       uk_list_add(&loader->list_head, &uk_binfmt_loader_list);
+
+       return 0;
+}
+
+int uk_binfmt_load(struct uk_binfmt_loader_args *args)
+{
+       struct uk_binfmt_loader *ldr;
+       int rc;
+
+       UK_ASSERT(args);
+       UK_ASSERT(args->pathname);
+       UK_ASSERT(args->alloc);
+       UK_ASSERT(args->ctx.sp);
+       UK_ASSERT(args->stack_size);
+       UK_ASSERT(!args->user);
+       UK_ASSERT(!args->loader);
+
+       /* Try SCRIPT types first. These should update args and
+        * fall back to EXEC types.
+        */
+       uk_binfmt_loader_foreach(ldr, UK_BINFMT_LOADER_TYPE_SCRIPT) {
+               UK_ASSERT(ldr->ops.load);
+               rc = ldr->ops.load(args);
+               if (unlikely(rc < 0)) {
+                       uk_pr_err("binfmt loader error (%d)\n", rc);
+                       return rc;
+               }
+               if (rc == UK_BINFMT_HANDLED_CONT) {
+                       /* For now restrict the use of private data
+                        * to the final loader.
+                        */
+                       UK_ASSERT(!args->user);
+                       break; /* Assume one interpreter max */
+               }
+               if (rc == UK_BINFMT_HANDLED)
+                       return 0;
+       }
+
+       uk_binfmt_loader_foreach(ldr, UK_BINFMT_LOADER_TYPE_EXEC) {
+               rc = ldr->ops.load(args);
+               if (unlikely(rc < 0)) {
+                       uk_pr_err("binfmt loader error (%d)\n", rc);
+                       return rc;
+               }
+               /* Only one loader of TYPE_EXEC is expected
+                * to handle a binary.
+                */
+               UK_ASSERT(rc != UK_BINFMT_HANDLED_CONT);
+               if (rc == UK_BINFMT_HANDLED) {
+                       uk_pr_debug("Load handled by %s\n", ldr->name);
+                       args->loader = ldr;
+                       return 0;
+               }
+       }
+
+       uk_pr_err("%s: Loader not found\n", args->pathname);
+
+       return -ENOEXEC;
+}
+
+int uk_binfmt_unload(struct uk_binfmt_loader_args *args)
+{
+       int rc;
+
+       UK_ASSERT(args);
+       UK_ASSERT(args->loader);
+
+       uk_pr_debug("Unload handled by %s\n", args->loader->name);
+       rc = args->loader->ops.unload(args);
+       if (unlikely(rc < 0)) {
+               uk_pr_err("binfmt unload error (%d)\n", rc);
+               return rc;
+       }
+
+       UK_ASSERT(rc != UK_BINFMT_HANDLED_CONT);
+
+       return 0;
+}
diff --git a/lib/ukbinfmt/include/uk/binfmt.h b/lib/ukbinfmt/include/uk/binfmt.h
new file mode 100644 (file)
index 0000000..a642c5d
--- /dev/null
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2024, Unikraft GmbH and The Unikraft Authors.
+ * Licensed under the BSD-3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ */
+
+#ifndef __UK_BINFMT_H__
+#define __UK_BINFMT_H__
+
+#include <uk/alloc.h>
+#include <uk/arch/ctx.h>
+#include <uk/essentials.h>
+#include <uk/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UK_BINFMT_NOT_HANDLED  0  /* Not handled. Try next loader. */
+#define UK_BINFMT_HANDLED      1  /* Handled. Stop passing to loaders. */
+#define UK_BINFMT_HANDLED_CONT 2  /* Handled. Pass to next loader. */
+
+/** Loader type */
+enum uk_binfmt_type {
+       UK_BINFMT_LOADER_TYPE_NONE,
+       UK_BINFMT_LOADER_TYPE_SCRIPT,
+       UK_BINFMT_LOADER_TYPE_EXEC,
+};
+
+struct uk_binfmt_loader_args {
+       const char *pathname; /* fully qualified */
+       char *progname;
+       int argc, envc;
+       const char **argv;
+       const char **envp;
+       struct uk_alloc *alloc;
+       struct ukarch_ctx ctx;
+       __sz stack_size;
+       struct uk_binfmt_loader *loader;
+       void *user; /* private loader data */
+};
+
+/* Loader load function.
+ *
+ * @param args[in|out] Loader args. May be updated by the loader.
+ * @return
+ *         - UK_BINFMT_NOT_HANDLED if this loader does not handle this file type
+ *         - UK_BINFMT_HANDLED if handled (final)
+ *         - UK_BINFMT_HANDLED_CONT if handled and should be passed to the next
+ *                                  loader. Can only be returned by loaders of
+ *                                  TYPE_SCRIPT. Upon returning this value the
+ *                                  next loader is searched among loaders of
+ *                                  TYPE_EXEC.
+ *         - Negative value on load error
+ */
+typedef        int (*uk_binfmt_load_fn)(struct uk_binfmt_loader_args *args);
+
+/* Loader unload function.
+ *
+ * @param args Loader args.
+ * @return zero on success or negative value on error.
+ */
+typedef        int (*uk_binfmt_unload_fn)(struct uk_binfmt_loader_args *args);
+
+struct uk_binfmt_loader {
+       /** loader name */
+       const char *name;
+       /** loader type (see ::uk_binfmt_type) */
+       enum uk_binfmt_type type;
+       /** loader ops */
+       struct {
+               uk_binfmt_load_fn load;
+               uk_binfmt_load_fn unload;
+       } ops;
+       struct uk_list_head list_head;
+};
+
+/**
+ * Register an interpreter
+ *
+ * @param loader pointer to loader descriptor.
+ * @return zero on success, negative value on error.
+ */
+int uk_binfmt_register(struct uk_binfmt_loader *loader);
+
+/**
+ * Load an executable using a suitable loader
+ *
+ * @param args[in|out] Loader args. May be updated by the loader
+ * @return zero on success, negative value on error
+ */
+int uk_binfmt_load(struct uk_binfmt_loader_args *args);
+
+/**
+ * Unload executable
+ *
+ * @param args[in|out] Loader args. May be updated by the loader
+ * @return zero on success, negative value on error
+ */
+int uk_binfmt_unload(struct uk_binfmt_loader_args *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UK_BINFMT_H__ */