Commit ecaa6ddf authored by Gary Guo's avatar Gary Guo Committed by Miguel Ojeda
Browse files

rust: add `build_error` crate



The `build_error` crate provides a function `build_error` which
will panic at compile-time if executed in const context and,
by default, will cause a build error if not executed at compile
time and the optimizer does not optimise away the call.

The `CONFIG_RUST_BUILD_ASSERT_ALLOW` kernel option allows to
relax the default build failure and convert it to a runtime
check. If the runtime check fails, `panic!` will be called.

Its functionality will be exposed to users as a couple macros in
the `kernel` crate in the following patch, thus some documentation
here refers to them for simplicity.

Signed-off-by: default avatarGary Guo <gary@garyguo.net>
Reviewed-by: default avatarWei Liu <wei.liu@kernel.org>
[Reworded, adapted for upstream and applied latest changes]
Signed-off-by: default avatarMiguel Ojeda <ojeda@kernel.org>
parent ef9e3797
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -2801,6 +2801,22 @@ config RUST_OVERFLOW_CHECKS

	  If unsure, say Y.

config RUST_BUILD_ASSERT_ALLOW
	bool "Allow unoptimized build-time assertions"
	depends on RUST
	help
	  Controls how are `build_error!` and `build_assert!` handled during build.

	  If calls to them exist in the binary, it may indicate a violated invariant
	  or that the optimizer failed to verify the invariant during compilation.

	  This should not happen, thus by default the build is aborted. However,
	  as an escape hatch, you can choose Y here to ignore them during build
	  and let the check be carried at runtime (with `panic!` being called if
	  the check fails).

	  If unsure, say N.

endmenu # "Rust"

source "Documentation/Kconfig"
+17 −5
Original line number Diff line number Diff line
@@ -19,6 +19,12 @@ obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o
always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \
    exports_kernel_generated.h

ifdef CONFIG_RUST_BUILD_ASSERT_ALLOW
obj-$(CONFIG_RUST) += build_error.o
else
always-$(CONFIG_RUST) += build_error.o
endif

obj-$(CONFIG_RUST) += exports.o

# Avoids running `$(RUSTC)` for the sysroot when it may not be available.
@@ -108,7 +114,7 @@ rustdoc-alloc: $(src)/alloc/lib.rs rustdoc-core rustdoc-compiler_builtins FORCE
	$(call if_changed,rustdoc)

rustdoc-kernel: private rustc_target_flags = --extern alloc \
    --extern macros=$(objtree)/$(obj)/libmacros.so \
    --extern build_error --extern macros=$(objtree)/$(obj)/libmacros.so \
    --extern bindings
rustdoc-kernel: $(src)/kernel/lib.rs rustdoc-core rustdoc-macros \
    rustdoc-compiler_builtins rustdoc-alloc $(obj)/libmacros.so \
@@ -126,6 +132,9 @@ quiet_cmd_rustc_test_library = RUSTC TL $<
		-L$(objtree)/$(obj)/test \
		--crate-name $(subst rusttest-,,$(subst rusttestlib-,,$@)) $<

rusttestlib-build_error: $(src)/build_error.rs rusttest-prepare FORCE
	$(call if_changed,rustc_test_library)

rusttestlib-macros: private rustc_target_flags = --extern proc_macro
rusttestlib-macros: private rustc_test_library_proc = yes
rusttestlib-macros: $(src)/macros/lib.rs rusttest-prepare FORCE
@@ -216,9 +225,9 @@ rusttest-macros: $(src)/macros/lib.rs rusttest-prepare FORCE
	$(call if_changed,rustdoc_test)

rusttest-kernel: private rustc_target_flags = --extern alloc \
    --extern macros --extern bindings
    --extern build_error --extern macros --extern bindings
rusttest-kernel: $(src)/kernel/lib.rs rusttest-prepare \
    rusttestlib-macros rusttestlib-bindings FORCE
    rusttestlib-build_error rusttestlib-macros rusttestlib-bindings FORCE
	$(call if_changed,rustc_test)
	$(call if_changed,rustc_test_library)

@@ -366,6 +375,9 @@ $(obj)/alloc.o: private rustc_target_flags = $(alloc-cfgs)
$(obj)/alloc.o: $(src)/alloc/lib.rs $(obj)/compiler_builtins.o FORCE
	$(call if_changed_dep,rustc_library)

$(obj)/build_error.o: $(src)/build_error.rs $(obj)/compiler_builtins.o FORCE
	$(call if_changed_dep,rustc_library)

$(obj)/bindings.o: $(src)/bindings/lib.rs \
    $(obj)/compiler_builtins.o \
    $(obj)/bindings/bindings_generated.rs \
@@ -373,8 +385,8 @@ $(obj)/bindings.o: $(src)/bindings/lib.rs \
	$(call if_changed_dep,rustc_library)

$(obj)/kernel.o: private rustc_target_flags = --extern alloc \
    --extern macros --extern bindings
$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o \
    --extern build_error --extern macros --extern bindings
$(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/alloc.o $(obj)/build_error.o \
    $(obj)/libmacros.so $(obj)/bindings.o FORCE
	$(call if_changed_dep,rustc_library)

rust/build_error.rs

0 → 100644
+31 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

//! Build-time error.
//!
//! This crate provides a [const function][const-functions] `build_error`, which will panic in
//! compile-time if executed in [const context][const-context], and will cause a build error
//! if not executed at compile time and the optimizer does not optimise away the call.
//!
//! It is used by `build_assert!` in the kernel crate, allowing checking of
//! conditions that could be checked statically, but could not be enforced in
//! Rust yet (e.g. perform some checks in [const functions][const-functions], but those
//! functions could still be called in the runtime).
//!
//! For details on constant evaluation in Rust, please see the [Reference][const-eval].
//!
//! [const-eval]: https://doc.rust-lang.org/reference/const_eval.html
//! [const-functions]: https://doc.rust-lang.org/reference/const_eval.html#const-functions
//! [const-context]: https://doc.rust-lang.org/reference/const_eval.html#const-context

#![no_std]

/// Panics if executed in [const context][const-context], or triggers a build error if not.
///
/// [const-context]: https://doc.rust-lang.org/reference/const_eval.html#const-context
#[inline(never)]
#[cold]
#[export_name = "rust_build_error"]
#[track_caller]
pub const fn build_error(msg: &'static str) -> ! {
    panic!("{}", msg);
}
+5 −0
Original line number Diff line number Diff line
@@ -19,3 +19,8 @@
#include "exports_alloc_generated.h"
#include "exports_bindings_generated.h"
#include "exports_kernel_generated.h"

// For modules using `rust/build_error.rs`.
#ifdef CONFIG_RUST_BUILD_ASSERT_ALLOW
EXPORT_SYMBOL_RUST_GPL(rust_build_error);
#endif
+7 −1
Original line number Diff line number Diff line
@@ -67,6 +67,12 @@ def generate_crates(srctree, objtree, sysroot_src):
    )
    crates[-1]["proc_macro_dylib_path"] = "rust/libmacros.so"

    append_crate(
        "build_error",
        srctree / "rust" / "build_error.rs",
        ["core", "compiler_builtins"],
    )

    append_crate(
        "bindings",
        srctree / "rust"/ "bindings" / "lib.rs",
@@ -78,7 +84,7 @@ def generate_crates(srctree, objtree, sysroot_src):
    append_crate(
        "kernel",
        srctree / "rust" / "kernel" / "lib.rs",
        ["core", "alloc", "macros", "bindings"],
        ["core", "alloc", "macros", "build_error", "bindings"],
        cfg=cfg,
    )
    crates[-1]["source"] = {