From: Michalis Pappas Date: Wed, 10 Apr 2024 09:55:20 +0000 (+0200) Subject: lib: Introduce libukbinfmt X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=3424f74ff02cd4ba30ed666ec2974fd81aa947cd;p=unikraft%2Funikraft.git lib: Introduce libukbinfmt 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 Approved-by: Andrei Tatar Reviewed-by: Sergiu Moga Reviewed-by: Andrei Tatar GitHub-Closes: #1386 --- diff --git a/lib/Makefile.uk b/lib/Makefile.uk index d297030fb..e2a14e553 100644 --- a/lib/Makefile.uk +++ b/lib/Makefile.uk @@ -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 index 000000000..850482822 --- /dev/null +++ b/lib/ukbinfmt/Config.uk @@ -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 index 000000000..2c9baad11 --- /dev/null +++ b/lib/ukbinfmt/Makefile.uk @@ -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 index 000000000..b5f4aecf7 --- /dev/null +++ b/lib/ukbinfmt/binfmt.c @@ -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 + +#include +#include +#include + +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 index 000000000..a642c5d86 --- /dev/null +++ b/lib/ukbinfmt/include/uk/binfmt.h @@ -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 +#include +#include +#include + +#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__ */