$(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))
--- /dev/null
+menuconfig LIBUKBINFMT
+ bool "ukbinfmt: Register loaders for executables"
+ help
+ libukbinfmt provides a minimal framework to register loaders of
+ different types of executable files
--- /dev/null
+$(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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */