# SPDX-License-Identifier: BSD-3-Clause # # Build glue for the Rust ELF loader. # # The loader core is a `no_std` Rust staticlib; `glue/shim.c` is the only C # that talks to Unikraft's headers. Unikraft links the archive through # `ALIBS-y`, which `buildrule_olib` already wraps in --start-group. $(eval $(call addlib,appelfloader)) APPELFLOADER_CFLAGS-$(CONFIG_APPELFLOADER_DEBUG) += -DUK_DEBUG APPELFLOADER_SRCS-y += $(APPELFLOADER_BASE)/glue/shim.c APPELFLOADER_SRCS-$(CONFIG_APPELFLOADER_VDSO) += $(APPELFLOADER_BUILD)/vdso-image.c APPELFLOADER_SRCS-$(CONFIG_APPELFLOADER_VDSO) += $(APPELFLOADER_BASE)/vdso/vsyscall.c ################################################################################ # The Rust loader core ################################################################################ # # Bare-metal targets, not *-unknown-linux-*: the unikernel has no OS below it, # and Unikraft compiles kernel code without FP/SIMD (-mgeneral-regs-only on # arm64, -mno-sse on x86_64). The `softfloat`/`none` targets match that ABI, so # the archive can be linked with the rest of the kernel without the compiler # spilling a NEON register into an interrupt-time code path. # APPELFLOADER_RUST_DIR := $(APPELFLOADER_BASE)/rust # Cargo's target directory. Overridable (and inherited from the environment, # hence `?=`) so a caller can point it at a cache that outlives the Unikraft # build tree -- the loader is the same archive for a given source revision, # target and feature set, and rebuilding it on every kernel build is pure # waste. bsdkrun's build.sh mounts a host directory here. APPELFLOADER_RUST_CACHE ?= $(APPELFLOADER_BUILD)/rust ifeq ($(CONFIG_ARCH_ARM_64),y) APPELFLOADER_RUST_TARGET := aarch64-unknown-none-softfloat else ifeq ($(CONFIG_ARCH_X86_64),y) APPELFLOADER_RUST_TARGET := x86_64-unknown-none else $(error app-elfloader-rs supports arm64 and x86_64 only) endif # What cargo produces, in the (possibly shared) cache... APPELFLOADER_RUST_BUILT := \ $(APPELFLOADER_RUST_CACHE)/$(APPELFLOADER_RUST_TARGET)/release/libelfloader_rs.a # ...and the copy this build links, inside the build tree. APPELFLOADER_RUST_LIB := $(APPELFLOADER_BUILD)/libelfloader_rs.a # Kconfig symbols the Rust side needs at *compile* time, because they decide # which C the archive is allowed to reference. CONFIG_APPELFLOADER_ELF_BINFMT # guards the `uk_binfmt_loader_args` accessors in glue/shim.c, so `binfmt.rs` # has to be compiled out with them. APPELFLOADER_RUST_FEATURES-$(CONFIG_APPELFLOADER_ELF_BINFMT) += binfmt APPELFLOADER_RUST_FEATURE_FLAGS := $(if $(APPELFLOADER_RUST_FEATURES-y), \ --features $(subst $(space),$(comma),$(strip $(APPELFLOADER_RUST_FEATURES-y)))) # $(UK_CONFIG) is a prerequisite because the *feature set* is derived from # Kconfig: without it, toggling CONFIG_APPELFLOADER_ELF_BINFMT leaves the # previously built archive in place and the link fails on the binfmt entry # points that glue/shim.c has just started referencing. APPELFLOADER_RUST_SRCS := \ $(wildcard $(APPELFLOADER_RUST_DIR)/src/*.rs) \ $(APPELFLOADER_RUST_DIR)/Cargo.toml \ $(APPELFLOADER_RUST_DIR)/rust-toolchain.toml \ $(UK_CONFIG) # CARGO can be overridden for a pinned toolchain (`make CARGO="cargo +1.83"`). CARGO ?= cargo ifeq ($(shell command -v $(CARGO) 2>/dev/null),) $(error app-elfloader-rs needs cargo. Install it with: \ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ and make sure ~/.cargo/bin is on PATH) endif # --offline is safe because the crate has no dependencies; it keeps the build # from reaching for a registry it does not need. --locked makes that explicit. # # The archive is only copied into the build tree when it actually differs, so # an unchanged loader does not give the 130 MB unikernel a newer prerequisite # and force a relink. cargo's own up-to-date check makes the build itself a # no-op in that case, so the whole rule costs well under a second. # # The copy uses `verbose_cmd_inner`, not `verbose_cmd`: the latter expands to a # string starting with `@`, which make only strips at the start of a recipe # line. Anywhere else -- such as inside this `if` -- the shell is handed a # literal `@printf` and the rule dies with "command not found". $(APPELFLOADER_RUST_LIB): $(APPELFLOADER_RUST_SRCS) $(call verbose_cmd,CARGO,appelfloader: $(notdir $(APPELFLOADER_RUST_BUILT)), \ $(CARGO) build \ --manifest-path $(APPELFLOADER_RUST_DIR)/Cargo.toml \ --target $(APPELFLOADER_RUST_TARGET) \ --target-dir $(APPELFLOADER_RUST_CACHE) \ $(APPELFLOADER_RUST_FEATURE_FLAGS) \ --release --locked --offline) $(Q)if ! cmp -s $(APPELFLOADER_RUST_BUILT) $@ 2>/dev/null; then \ $(call verbose_cmd_inner,CACHE,appelfloader: $(notdir $@), \ cp $(APPELFLOADER_RUST_BUILT) $@); \ fi APPELFLOADER_ALIBS-y += $(APPELFLOADER_RUST_LIB) # The cache is deliberately not in CLEAN: it is shared across builds, and # `make clean` should not throw away work that is still valid. APPELFLOADER_CLEAN += $(APPELFLOADER_RUST_LIB) # Run the loader's own tests on the build host. Not wired into `all`: it builds # for the host triple, which is a different compilation from the one above. .PHONY: appelfloader-test appelfloader-test: $(CARGO) test --manifest-path $(APPELFLOADER_RUST_DIR)/Cargo.toml ################################################################################ # vDSO (unchanged from the C loader: a build-time asset, not loader logic) ################################################################################ $(APPELFLOADER_BUILD)/vdso.o: $(APPELFLOADER_BASE)/vdso/vdso.c $(call build_cmd,CC,appelfloader,$(notdir $@), \ $(CC) $< -c -o $@ -fPIC -O2 -nostdlib) $(APPELFLOADER_BUILD)/vdso.patched.o: $(APPELFLOADER_BUILD)/vdso.o $(APPELFLOADER_BASE)/vdso/vdso_mapping.conf $(call build_cmd,ADDSYM,appelfloader,$(notdir $@), \ OBJCOPY_CMD=$(OBJCOPY) APPELFLOADER_BASE=$(APPELFLOADER_BASE) VDSO_MAGIC_NUMBER=0x369C217100000000 $(APPELFLOADER_BASE)/vdso/add_symbol.sh $(APPELFLOADER_BASE)/vdso/vdso_mapping.conf $< $@) $(APPELFLOADER_BUILD)/libvdso.so: $(APPELFLOADER_BUILD)/vdso.patched.o $(APPELFLOADER_BASE)/vdso/vdso.lds $(call build_cmd,LD,appelfloader,$(notdir $@), \ $(LD) $< -o $@ -nostdlib -Wl$(comma)--hash-style=both -Wl$(comma)-soname=unikraft-vdso.so.1 -Wl$(comma)-shared -Wl$(comma)-T$(comma)$(APPELFLOADER_BASE)/vdso/vdso.lds) $(APPELFLOADER_BUILD)/vdso-image.c: $(APPELFLOADER_BUILD)/libvdso.so $(APPELFLOADER_BASE)/vdso/vdso_mapping.conf $(call build_cmd,PYTHON,appelfloader,$(notdir $@), \ $(PYTHON) $(APPELFLOADER_BASE)/vdso/bin2c.py $(APPELFLOADER_BASE)/vdso/vdso_mapping.conf $< $@) APPELFLOADER_CLEAN += $(APPELFLOADER_BUILD)/vdso.o APPELFLOADER_CLEAN += $(APPELFLOADER_BUILD)/vdso.patched.o APPELFLOADER_CLEAN += $(APPELFLOADER_BUILD)/libvdso.so APPELFLOADER_CLEAN += $(APPELFLOADER_BUILD)/vdso-image.c ################################################################################ # /etc autogeneration (unchanged, still C) ################################################################################ APPELFLOADER_SRCS-$(CONFIG_APPELFLOADER_AUTOGEN) += $(APPELFLOADER_BASE)/autogen/conffile.c ifneq ($(filter y,$(CONFIG_APPELFLOADER_AUTOGEN_ETCRESOLVCONF) \ $(CONFIG_APPELFLOADER_AUTOGEN_ETCHOSTS) \ $(CONFIG_APPELFLOADER_AUTOGEN_ETCHOSTNAME) \ ),) APPELFLOADER_SRCS-$(CONFIG_APPELFLOADER_AUTOGEN) += $(APPELFLOADER_BASE)/autogen/etc.c endif