Skip to content

Commit da31291

Browse files
authored
Merge pull request #257 from nbdd0121/link-time-panic
Build-time assertion
2 parents fb672e0 + 5043400 commit da31291

17 files changed

+201
-2
lines changed

.github/workflows/kernel-arm-debug.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,5 +1708,8 @@ CONFIG_RUST_OPT_LEVEL_2=y
17081708
# CONFIG_RUST_OPT_LEVEL_3 is not set
17091709
# CONFIG_RUST_OPT_LEVEL_S is not set
17101710
# CONFIG_RUST_OPT_LEVEL_Z is not set
1711+
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
1712+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
1713+
CONFIG_RUST_BUILD_ASSERT_DENY=y
17111714
# end of Rust hacking
17121715
# end of Kernel hacking

.github/workflows/kernel-arm-release.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,5 +1666,8 @@ CONFIG_RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C=y
16661666
# CONFIG_RUST_OPT_LEVEL_3 is not set
16671667
# CONFIG_RUST_OPT_LEVEL_S is not set
16681668
# CONFIG_RUST_OPT_LEVEL_Z is not set
1669+
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
1670+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
1671+
CONFIG_RUST_BUILD_ASSERT_DENY=y
16691672
# end of Rust hacking
16701673
# end of Kernel hacking

.github/workflows/kernel-arm64-debug.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,5 +1466,8 @@ CONFIG_RUST_OPT_LEVEL_1=y
14661466
# CONFIG_RUST_OPT_LEVEL_3 is not set
14671467
# CONFIG_RUST_OPT_LEVEL_S is not set
14681468
# CONFIG_RUST_OPT_LEVEL_Z is not set
1469+
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
1470+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
1471+
CONFIG_RUST_BUILD_ASSERT_DENY=y
14691472
# end of Rust hacking
14701473
# end of Kernel hacking

.github/workflows/kernel-arm64-release.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,5 +1383,8 @@ CONFIG_RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C=y
13831383
# CONFIG_RUST_OPT_LEVEL_3 is not set
13841384
# CONFIG_RUST_OPT_LEVEL_S is not set
13851385
# CONFIG_RUST_OPT_LEVEL_Z is not set
1386+
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
1387+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
1388+
CONFIG_RUST_BUILD_ASSERT_DENY=y
13861389
# end of Rust hacking
13871390
# end of Kernel hacking

.github/workflows/kernel-ppc64le-debug.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,5 +1588,7 @@ CONFIG_RUST_OPT_LEVEL_0=y
15881588
# CONFIG_RUST_OPT_LEVEL_3 is not set
15891589
# CONFIG_RUST_OPT_LEVEL_S is not set
15901590
# CONFIG_RUST_OPT_LEVEL_Z is not set
1591+
CONFIG_RUST_BUILD_ASSERT_ALLOW=y
1592+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
15911593
# end of Rust hacking
15921594
# end of Kernel hacking

.github/workflows/kernel-ppc64le-release.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,5 +1491,8 @@ CONFIG_RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C=y
14911491
# CONFIG_RUST_OPT_LEVEL_3 is not set
14921492
# CONFIG_RUST_OPT_LEVEL_S is not set
14931493
# CONFIG_RUST_OPT_LEVEL_Z is not set
1494+
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
1495+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
1496+
CONFIG_RUST_BUILD_ASSERT_DENY=y
14941497
# end of Rust hacking
14951498
# end of Kernel hacking

.github/workflows/kernel-riscv64-debug.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,5 +1317,7 @@ CONFIG_RUST_OPT_LEVEL_0=y
13171317
# CONFIG_RUST_OPT_LEVEL_3 is not set
13181318
# CONFIG_RUST_OPT_LEVEL_S is not set
13191319
# CONFIG_RUST_OPT_LEVEL_Z is not set
1320+
CONFIG_RUST_BUILD_ASSERT_ALLOW=y
1321+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
13201322
# end of Rust hacking
13211323
# end of Kernel hacking

.github/workflows/kernel-riscv64-release.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,5 +1231,8 @@ CONFIG_RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C=y
12311231
# CONFIG_RUST_OPT_LEVEL_3 is not set
12321232
# CONFIG_RUST_OPT_LEVEL_S is not set
12331233
# CONFIG_RUST_OPT_LEVEL_Z is not set
1234+
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
1235+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
1236+
CONFIG_RUST_BUILD_ASSERT_DENY=y
12341237
# end of Rust hacking
12351238
# end of Kernel hacking

.github/workflows/kernel-x86_64-debug.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,5 +1542,7 @@ CONFIG_RUST_OPT_LEVEL_0=y
15421542
# CONFIG_RUST_OPT_LEVEL_3 is not set
15431543
# CONFIG_RUST_OPT_LEVEL_S is not set
15441544
# CONFIG_RUST_OPT_LEVEL_Z is not set
1545+
CONFIG_RUST_BUILD_ASSERT_ALLOW=y
1546+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
15451547
# end of Rust hacking
15461548
# end of Kernel hacking

.github/workflows/kernel-x86_64-release.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,5 +1433,8 @@ CONFIG_RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C=y
14331433
# CONFIG_RUST_OPT_LEVEL_3 is not set
14341434
# CONFIG_RUST_OPT_LEVEL_S is not set
14351435
# CONFIG_RUST_OPT_LEVEL_Z is not set
1436+
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
1437+
# CONFIG_RUST_BUILD_ASSERT_WARN is not set
1438+
CONFIG_RUST_BUILD_ASSERT_DENY=y
14361439
# end of Rust hacking
14371440
# end of Kernel hacking

lib/Kconfig.debug

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,43 @@ config RUST_OPT_LEVEL_Z
26412641

26422642
endchoice
26432643

2644+
choice
2645+
prompt "Build-time assertions"
2646+
default RUST_BUILD_ASSERT_ALLOW if RUST_OPT_LEVEL_0
2647+
default RUST_BUILD_ASSERT_DENY if !RUST_OPT_LEVEL_0
2648+
help
2649+
Controls how are `build_error!` and `build_assert!` handled during build.
2650+
2651+
If calls to them exist in the binary, it may indicate a violated invariant
2652+
or that the optimizer failed to verify the invariant during compilation.
2653+
You can choose to abort compilation or ignore them during build and let the
2654+
check be carried to runtime.
2655+
2656+
If optimizations are turned off, you cannot select "Deny".
2657+
2658+
If unsure, say "Deny".
2659+
2660+
config RUST_BUILD_ASSERT_ALLOW
2661+
bool "Allow"
2662+
help
2663+
Unoptimized calls to `build_error!` will be converted to `panic!`
2664+
and checked at runtime.
2665+
2666+
config RUST_BUILD_ASSERT_WARN
2667+
bool "Warn"
2668+
help
2669+
Unoptimized calls to `build_error!` will be converted to `panic!`
2670+
and checked at runtime, but warnings will be generated when building.
2671+
2672+
config RUST_BUILD_ASSERT_DENY
2673+
bool "Deny"
2674+
depends on !RUST_OPT_LEVEL_0
2675+
help
2676+
Unoptimized calls to `build_error!` will abort compilation.
2677+
2678+
endchoice
2679+
2680+
26442681
endmenu # "Rust"
26452682

26462683
source "Documentation/Kconfig"

rust/Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ extra-$(CONFIG_RUST) += bindings_generated.rs
99
obj-$(CONFIG_RUST) += alloc.o kernel.o
1010
extra-$(CONFIG_RUST) += exports_alloc_generated.h exports_kernel_generated.h
1111

12+
ifndef CONFIG_RUST_BUILD_ASSERT_DENY
13+
obj-$(CONFIG_RUST) += build_error.o
14+
endif
15+
1216
obj-$(CONFIG_RUST) += exports.o
1317

1418
RUSTDOC = rustdoc
@@ -40,6 +44,7 @@ rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE
4044
$(call if_changed,rustdoc)
4145

4246
rustdoc-kernel: private rustdoc_target_flags = --extern alloc \
47+
--extern build_error \
4348
--extern module=$(objtree)/rust/libmodule.so
4449
rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-module \
4550
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
@@ -77,7 +82,7 @@ bindgen_c_flags = $(filter-out $(bindgen_skip_c_flags), $(c_flags)) \
7782
$(bindgen_extra_c_flags)
7883
endif
7984

80-
bindgen_opaque_types := xregs_state desc_struct arch_lbr_state
85+
bindgen_opaque_types := xregs_state desc_struct arch_lbr_state local_apic
8186

8287
# To avoid several recompilations in PowerPC, which inserts `-D_TASK_CPU`
8388
bindgen_c_flags_final = $(filter-out -D_TASK_CPU=%, $(bindgen_c_flags))
@@ -155,9 +160,15 @@ $(objtree)/rust/alloc.o: $$(RUST_LIB_SRC)/alloc/src/lib.rs \
155160
$(objtree)/rust/compiler_builtins.o FORCE
156161
$(call if_changed_dep,rustc_library)
157162

163+
$(objtree)/rust/build_error.o: $(srctree)/rust/build_error.rs \
164+
$(objtree)/rust/compiler_builtins.o FORCE
165+
$(call if_changed_dep,rustc_library)
166+
158167
# ICE on `--extern module`: https://github.com/rust-lang/rust/issues/56935
159168
$(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \
169+
--extern build_error \
160170
--extern module=$(objtree)/rust/libmodule.so
161171
$(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \
172+
$(objtree)/rust/build_error.o \
162173
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
163174
$(call if_changed_dep,rustc_library)

rust/build_error.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Build-time error.
4+
//!
5+
//! This crate provides a function `build_error`, which will panic in
6+
//! compile-time if executed in const context, and will cause a build error
7+
//! if not executed at compile time and the optimizer does not optimise away the
8+
//! call.
9+
//!
10+
//! It is used by `build_assert!` in the kernel crate, allowing checking of
11+
//! conditions that could be checked statically, but could not be enforced in
12+
//! Rust yet (e.g. perform some checks in const functions, but those
13+
//! functions could still be called in the runtime).
14+
15+
#![no_std]
16+
#![feature(const_panic, core_panic)]
17+
18+
/// Panics if executed in const context, or triggers a build error if not.
19+
#[inline(never)]
20+
#[cold]
21+
#[no_mangle]
22+
#[track_caller]
23+
pub const fn build_error(msg: &'static str) -> ! {
24+
// Could also be `panic!(msg)` to avoid using unstable feature `core_panic`,
25+
// but it is not allowed in Rust 2021, while `panic!("{}", msg)` could not
26+
// yet be used in const context.
27+
core::panicking::panic(msg);
28+
}
29+
30+
#[cfg(CONFIG_RUST_BUILD_ASSERT_WARN)]
31+
#[link_section = ".gnu.warning.build_error"]
32+
#[used]
33+
static BUILD_ERROR_WARNING: [u8; 45] = *b"call to build_error present after compilation";

rust/kernel/build_assert.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Build-time assert.
4+
5+
/// Fails the build if the code path calling `build_error!` can possibly be executed.
6+
///
7+
/// If the macro is executed in const context, `build_error!` will panic.
8+
/// If the compiler or optimizer cannot guarantee that `build_error!` can never
9+
/// be called, a build error will be triggered.
10+
///
11+
/// # Examples
12+
/// ```no_run
13+
/// #[inline]
14+
/// fn foo(a: usize) -> usize {
15+
/// a.checked_add(1).unwrap_or_else(|| build_error!("overflow"))
16+
/// }
17+
/// ```
18+
#[macro_export]
19+
macro_rules! build_error {
20+
() => {{
21+
$crate::build_error("")
22+
}};
23+
($msg:expr) => {{
24+
$crate::build_error($msg)
25+
}};
26+
}
27+
28+
/// Asserts that a boolean expression is `true` at compile time.
29+
///
30+
/// If the condition is evaluated to `false` in const context, `build_assert!`
31+
/// will panic. If the compiler or optimizer cannot guarantee the condition will
32+
/// be evaluated to `true`, a build error will be triggered.
33+
///
34+
/// [`static_assert!`] should be preferred to `build_assert!` whenever possible.
35+
///
36+
/// # Examples
37+
///
38+
/// These examples show that different types of [`assert!`] will trigger errors
39+
/// at different stage of compilation. It is preferred to err as early as
40+
/// possible, so [`static_assert!`] should be used whenever possible.
41+
/// ```no_run
42+
/// fn foo() {
43+
/// static_assert!(1 > 1); // Compile-time error
44+
/// build_assert!(1 > 1); // Build-time error
45+
/// assert!(1 > 1); // Run-time error
46+
/// }
47+
/// ```
48+
///
49+
/// When the condition refers to generic parameters or parameters of an inline function,
50+
/// [`static_assert!`] cannot be used. Use `build_assert!` in this scenario.
51+
/// ```no_run
52+
/// fn foo<const N: usize>() {
53+
/// // `static_assert!(N > 1);` is not allowed
54+
/// build_assert!(N > 1); // Build-time check
55+
/// assert!(N > 1); // Run-time check
56+
/// }
57+
///
58+
/// #[inline]
59+
/// fn bar(n: usize) {
60+
/// // `static_assert!(n > 1);` is not allowed
61+
/// build_assert!(n > 1); // Build-time check
62+
/// assert!(n > 1); // Run-time check
63+
/// }
64+
/// ```
65+
#[macro_export]
66+
macro_rules! build_assert {
67+
($cond:expr $(,)?) => {{
68+
if !$cond {
69+
$crate::build_error(concat!("assertion failed: ", stringify!($cond)));
70+
}
71+
}};
72+
($cond:expr, $msg:expr) => {{
73+
if !$cond {
74+
$crate::build_error($msg);
75+
}
76+
}};
77+
}

rust/kernel/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ mod raw_list;
5151
#[doc(hidden)]
5252
pub mod module_param;
5353

54+
mod build_assert;
5455
pub mod prelude;
5556
pub mod print;
5657
pub mod random;
@@ -66,6 +67,9 @@ pub mod platdev;
6667
mod types;
6768
pub mod user_ptr;
6869

70+
#[doc(hidden)]
71+
pub use build_error::build_error;
72+
6973
pub use crate::error::{Error, Result};
7074
pub use crate::types::{CStr, Mode};
7175

rust/kernel/prelude.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
1414
pub use alloc::{borrow::ToOwned, string::String};
1515

16+
pub use super::build_assert;
17+
1618
pub use module::{module, module_misc_device};
1719

1820
pub use super::{pr_alert, pr_cont, pr_crit, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};

scripts/generate_rust_analyzer.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,19 @@ def append_crate(display_name, root_module, is_workspace_member, deps, cfg):
7171
)
7272
crates[-1]["proc_macro_dylib_path"] = "rust/libmodule.so"
7373

74+
append_crate(
75+
"build_error",
76+
srctree / "rust" / "build_error.rs",
77+
True,
78+
["core", "compiler_builtins"],
79+
[],
80+
)
81+
7482
append_crate(
7583
"kernel",
7684
srctree / "rust" / "kernel" / "lib.rs",
7785
True,
78-
["core", "alloc", "module"],
86+
["core", "alloc", "module", "build_error"],
7987
cfg,
8088
)
8189
crates[-1]["env"]["RUST_BINDINGS_FILE"] = str(bindings_file.resolve(True))

0 commit comments

Comments
 (0)