Skip to contentSkip to conversation

Support several Rust toolchain versions

PATCH Applied Miguel Ojeda 2 participants 16 messages Jul 9, 2024 – Jul 10, 2024

Subsystems (7)
lore
Share & tools
lore

Miguel Ojeda

author maintainer
#0 lore raw

v2 cover Support several Rust toolchain versions

Hi all,

A few things improved here and there, and rebased on top of rust-next.

The changelog is attached to each patch.

I kept the Tested-bys since most of the changes are on documentation or comments, though I did remove them on the patch that changed the most just in case (even for that one, I think Benno's and Andreas' setup would not have made a difference).

I plan to put this series into rust-next very soon so that it goes into the merge window.

Previous cover letter:

v1: https://lore.kernel.org/rust-for-linux/[email protected]/

Thanks!

Cheers,
Miguel

Miguel Ojeda (13):
rust: macros: indent list item in paste!'s docs
rust: init: simplify from map_err to inspect_err
rust: allow dead_code for never constructed bindings
rust: relax most deny-level lints to warnings
rust: simplify Clippy warning flags set
rust: start supporting several compiler versions
rust: avoid assuming a particular bindgen build
rust: work around bindgen 0.69.0 issue
rust: start supporting several bindgen versions
rust: warn about bindgen versions 0.66.0 and 0.66.1
kbuild: rust: add rustc-version support
rust: support the new -Zub-checks flag
docs: rust: quick-start: add section on Linux distributions

 Documentation/process/changes.rst        |   9 +-
 Documentation/rust/quick-start.rst       | 136 +++++++++++++++++------
 Makefile                                 |  31 +++---
 init/Kconfig                             |  11 +-
 lib/Kconfig.debug                        |  18 +++
 rust/Makefile                            |   4 +-
 rust/bindings/lib.rs                     |   1 +
 rust/kernel/init.rs                      |  13 +--
 rust/macros/lib.rs                       |   2 +-
 rust/uapi/lib.rs                         |   1 +
 scripts/Kconfig.include                  |   6 +
 scripts/Makefile.compiler                |   4 +
 scripts/rust_is_available.sh             |  33 +++---
 scripts/rust_is_available_bindgen_0_66.h |   2 +
 scripts/rust_is_available_test.py        |  59 ++++++----
 scripts/rustc-version.sh                 |  52 +++++++++
 16 files changed, 274 insertions(+), 108 deletions(-)
 create mode 100644 scripts/rust_is_available_bindgen_0_66.h
 create mode 100755 scripts/rustc-version.sh

base-commit: fc6e66f4696b63b8a2645a2bcea407cb04bd0666 --
2.45.2

author maintainer
#1 lore raw

PATCH v2 1/13 rust: macros: indent list item in `paste!`'s docs

A new style lint, doc_lazy_continuation [1], has been introduced in the upcoming Rust 1.80 (currently in beta), which detects missing indentation in code documentation.

We have one such case:

error: doc list item missing indentation
--> rust/macros/lib.rs:315:5
|
315 | /// default the span of the [< >] group is used.
| ^
|
= help: if this is supposed to be its own paragraph, add a blank line
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
= note: -D clippy::doc-lazy-continuation implied by -D clippy::style
= help: to override -D clippy::style add #[allow(clippy::doc_lazy_continuation)]
help: indent this line
|
315 | /// default the span of the [< >] group is used.
| ++

While the rendering of the docs by rustdoc is not affected, we apply this kind of indentation elsewhere since it looks better.

Thus clean it up.

---
 rust/macros/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
rust/macros/lib.rs +1 -1
index 6dcc72aff111..159e75292970 100644
@@ -345,7 +345,7 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
 ///
 /// Currently supported modifiers are:
 /// * `span`: change the span of concatenated identifier to the span of the specified token. By
-/// default the span of the `[< >]` group is used.
+///   default the span of the `[< >]` group is used.
 /// * `lower`: change the identifier to lower case.
 /// * `upper`: change the identifier to upper case.
 ///
Signature · 1 line
2.45.2
author maintainer
#2 lore raw

PATCH v2 2/13 rust: init: simplify from `map_err` to `inspect_err`

In reply to Miguel Ojeda

A new complexity lint, manual_inspect [1], has been introduced in the upcoming Rust 1.81 (currently in nightly), which checks for uses of map* which return the original item:

    error:
    --> rust/kernel/init.rs:846:23
        |
    846 |         (self.1)(val).map_err(|e| {
        |                       ^^^^^^^
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect
        = note: `-D clippy::manual-inspect` implied by `-D warnings`
        = help: to override `-D warnings` add `#[allow(clippy::manual_inspect)]`
    help: try
        |
    846 ~         (self.1)(val).inspect_err(|e| {
    847 |             // SAFETY: `slot` was initialized above.
    848 ~             unsafe { core::ptr::drop_in_place(slot) };
        |

Thus clean them up.

---
v2:
- Moved SAFETY comment out of the closure, in one of the instances only. In the other, it would mean putting two SAFETY comments in a row, which is even more confusing. Open coding it may be clearer, though -- happy to get a patch for that later on (with some docs about guidelines for using or avoiding inspect_*). (Björn, Benno, Alice)

 rust/kernel/init.rs | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)
rust/kernel/init.rs +4 -9
index 68605b633e73..495c09ebe3a3 100644
@@ -843,11 +843,8 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
         let val = unsafe { &mut *slot };
         // SAFETY: `slot` is considered pinned.
         let val = unsafe { Pin::new_unchecked(val) };
-        (self.1)(val).map_err(|e| {
-            // SAFETY: `slot` was initialized above.
-            unsafe { core::ptr::drop_in_place(slot) };
-            e
-        })
+        // SAFETY: `slot` was initialized above.
+        (self.1)(val).inspect_err(|_| unsafe { core::ptr::drop_in_place(slot) })
     }
 }
 
@@ -941,11 +938,9 @@ unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
         // SAFETY: All requirements fulfilled since this function is `__init`.
         unsafe { self.0.__pinned_init(slot)? };
         // SAFETY: The above call initialized `slot` and we still have unique access.
-        (self.1)(unsafe { &mut *slot }).map_err(|e| {
+        (self.1)(unsafe { &mut *slot }).inspect_err(|_|
             // SAFETY: `slot` was initialized above.
-            unsafe { core::ptr::drop_in_place(slot) };
-            e
-        })
+            unsafe { core::ptr::drop_in_place(slot) })
     }
 }
 

--
2.45.2

author maintainer
#3 lore raw

PATCH v2 3/13 rust: allow `dead_code` for never constructed bindings

In reply to Miguel Ojeda

Starting with the upcoming Rust 1.80.0 (since upstream commit 35130d7233e9 ("Detect pub structs never constructed and unused associated constants in traits")), the dead_code pass detects more cases, which triggers in the bindings crate:

    warning: struct `boot_params` is never constructed
        --> rust/bindings/bindings_generated.rs:10684:12
        |
    10684 | pub struct boot_params {
        |            ^^^^^^^^^^^
        |
        = note: `#[warn(dead_code)]` on by default

As well as in the uapi one:

    warning: struct `boot_params` is never constructed
        --> rust/uapi/uapi_generated.rs:10392:12
        |
    10392 | pub struct boot_params {
        |            ^^^^^^^^^^^
        |
        = note: `#[warn(dead_code)]` on by default

These are all expected, since we do not use all the structs in the bindings that bindgen generates from the C headers.

Therefore, allow them.

---
 rust/bindings/lib.rs | 1 +
 rust/uapi/lib.rs     | 1 +
 2 files changed, 2 insertions(+)
rust/bindings/lib.rs +1 -0
index 40ddaee50d8b..93a1a3fc97bc 100644
@@ -24,6 +24,7 @@
     unsafe_op_in_unsafe_fn
 )]
 
+#[allow(dead_code)]
 mod bindings_raw {
     // Use glob import here to expose all helpers.
     // Symbols defined within the module will take precedence to the glob import.
rust/uapi/lib.rs +1 -0
index 0caad902ba40..80a00260e3e7 100644
@@ -14,6 +14,7 @@
 #![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))]
 #![allow(
     clippy::all,
+    dead_code,
     missing_docs,
     non_camel_case_types,
     non_upper_case_globals,
Signature · 1 line
2.45.2
author maintainer
#4 lore raw

PATCH v2 4/13 rust: relax most deny-level lints to warnings

In reply to Miguel Ojeda

Since we are starting to support several Rust toolchains, lints (including Clippy ones) now may behave differently and lint groups may include new lints.

Therefore, to maximize the chances a given version works, relax some deny-level lints to warnings. It may also make our lives a bit easier while developing new code or refactoring.

To be clear, the requirements for in-tree code are still the same, since Rust code still needs to be warning-free (patches should be clean under WERROR=y) and the set of lints is not changed.

unsafe_op_in_unsafe_fn is left unmodified, i.e. as an error, since it is becoming the default in the language (warn-by-default in Rust 2024 [1] and ideally an error later on) and thus it should also be very well tested. In addition, it is simple enough that it should not have false positives (unlike e.g. rust_2018_idioms's explicit_outlives_requirements).

non_ascii_idents is left unmodified as well, i.e. as an error, since it is unlikely one gains any productivity during development if it were a warning (in fact, it may be worse, since it is likely one made a typo). In addition, it should not have false positives.

Finally, put the two -D ones at the top and take the chance to do one per line.

---
v2:
- Kept non_ascii_idents as an error. (Björn, Finn)

 Makefile      | 24 +++++++++++++-----------
 rust/Makefile |  4 ++--
 2 files changed, 15 insertions(+), 13 deletions(-)
Makefile +13 -11
index fea263aaa492..7ea526814fdb 100644
@@ -461,17 +461,19 @@ KBUILD_USERLDFLAGS := $(USERLDFLAGS)
 # host programs.
 export rust_common_flags := --edition=2021 \
 			    -Zbinary_dep_depinfo=y \
-			    -Dunsafe_op_in_unsafe_fn -Drust_2018_idioms \
-			    -Dunreachable_pub -Dnon_ascii_idents \
+			    -Dunsafe_op_in_unsafe_fn \
+			    -Dnon_ascii_idents \
+			    -Wrust_2018_idioms \
+			    -Wunreachable_pub \
 			    -Wmissing_docs \
-			    -Drustdoc::missing_crate_level_docs \
-			    -Dclippy::correctness -Dclippy::style \
-			    -Dclippy::suspicious -Dclippy::complexity \
-			    -Dclippy::perf \
-			    -Dclippy::let_unit_value -Dclippy::mut_mut \
-			    -Dclippy::needless_bitwise_bool \
-			    -Dclippy::needless_continue \
-			    -Dclippy::no_mangle_with_rust_abi \
+			    -Wrustdoc::missing_crate_level_docs \
+			    -Wclippy::correctness -Wclippy::style \
+			    -Wclippy::suspicious -Wclippy::complexity \
+			    -Wclippy::perf \
+			    -Wclippy::let_unit_value -Wclippy::mut_mut \
+			    -Wclippy::needless_bitwise_bool \
+			    -Wclippy::needless_continue \
+			    -Wclippy::no_mangle_with_rust_abi \
 			    -Wclippy::dbg_macro
 
 KBUILD_HOSTCFLAGS   := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS)
@@ -572,7 +574,7 @@ KBUILD_RUSTFLAGS := $(rust_common_flags) \
 		    -Csymbol-mangling-version=v0 \
 		    -Crelocation-model=static \
 		    -Zfunction-sections=n \
-		    -Dclippy::float_arithmetic
+		    -Wclippy::float_arithmetic
 
 KBUILD_AFLAGS_KERNEL :=
 KBUILD_CFLAGS_KERNEL :=
rust/Makefile +2 -2
index 385378311322..bf05e65365da 100644
@@ -367,7 +367,7 @@ ifneq ($(or $(CONFIG_ARM64),$(and $(CONFIG_RISCV),$(CONFIG_64BIT))),)
 endif
 
 $(obj)/core.o: private skip_clippy = 1
-$(obj)/core.o: private skip_flags = -Dunreachable_pub
+$(obj)/core.o: private skip_flags = -Wunreachable_pub
 $(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym))
 $(obj)/core.o: private rustc_target_flags = $(core-cfgs)
 $(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs FORCE
@@ -381,7 +381,7 @@ $(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE
 	+$(call if_changed_dep,rustc_library)
 
 $(obj)/alloc.o: private skip_clippy = 1
-$(obj)/alloc.o: private skip_flags = -Dunreachable_pub
+$(obj)/alloc.o: private skip_flags = -Wunreachable_pub
 $(obj)/alloc.o: private rustc_target_flags = $(alloc-cfgs)
 $(obj)/alloc.o: $(RUST_LIB_SRC)/alloc/src/lib.rs $(obj)/compiler_builtins.o FORCE
 	+$(call if_changed_dep,rustc_library)

--
2.45.2

author maintainer
#5 lore raw

PATCH v2 5/13 rust: simplify Clippy warning flags set

In reply to Miguel Ojeda

All Clippy lint groups that we enable, except correctness, have a default warn level, thus they may be removed now that we relaxed all lints to warn.

Moreover, Clippy provides an all lint group that covers the groups we enable by default. Thus just use all instead -- the only change is that, if Clippy introduces a new lint group or splits an existing one, we will cover that one automatically.

In addition, let_unit_value is in style since Rust 1.62.0, thus it does not need to be enabled manually.

---
 Makefile | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)
Makefile +2 -4
index 7ea526814fdb..9044fdb9adb1 100644
@@ -467,10 +467,8 @@ export rust_common_flags := --edition=2021 \
 			    -Wunreachable_pub \
 			    -Wmissing_docs \
 			    -Wrustdoc::missing_crate_level_docs \
-			    -Wclippy::correctness -Wclippy::style \
-			    -Wclippy::suspicious -Wclippy::complexity \
-			    -Wclippy::perf \
-			    -Wclippy::let_unit_value -Wclippy::mut_mut \
+			    -Wclippy::all \
+			    -Wclippy::mut_mut \
 			    -Wclippy::needless_bitwise_bool \
 			    -Wclippy::needless_continue \
 			    -Wclippy::no_mangle_with_rust_abi \
Signature · 1 line
2.45.2
author maintainer
#6 lore raw

PATCH v2 6/13 rust: start supporting several compiler versions

In reply to Miguel Ojeda

It is time to start supporting several Rust compiler versions and thus establish a minimum Rust version.

We may still want to upgrade the minimum sometimes in the beginning since there may be important features coming into the language that improve how we write code (e.g. field projections), which may or may not make sense to support conditionally.

We will start with a window of two stable releases, and widen it over time. Thus this patch does not move the current minimum (1.78.0), but instead adds support for the recently released 1.79.0.

This should already be enough for kernel developers in distributions that provide recent Rust compiler versions routinely, such as Arch Linux, Debian Unstable (outside the freeze period), Fedora Linux, Gentoo Linux (especially the testing channel), Nix (unstable) and openSUSE Tumbleweed. See the documentation patch about it later in this series.

In addition, Rust for Linux is now being built-tested in Rust's pre-merge CI [1]. That is, every change that is attempting to land into the Rust compiler is tested against the kernel, and it is merged only if it passes -- thanks to the Rust project for that!

Thus, with the pre-merge CI in place, both projects hope to avoid unintentional changes to Rust that break the kernel. This means that, in general, apart from intentional changes on their side (that we will need to workaround conditionally on our side), the upcoming Rust compiler versions should generally work.

For instance, currently, the beta (1.80.0) and nightly (1.81.0) branches work as well.

Of course, the Rust for Linux CI job in the Rust toolchain may still need to be temporarily disabled for different reasons, but the intention is to help bring Rust for Linux into stable Rust.

---
v2:
- Update suggested rustup command to just install stable. (Finn)
- Update suggested command for installing manually the Rust standard library sources with the version that rustc reports, rather than the minimum (since now the user may have installed e.g. stable).

 Documentation/process/changes.rst  |  4 +---
 Documentation/rust/quick-start.rst | 15 +++++++--------
 scripts/rust_is_available.sh       |  8 --------
 scripts/rust_is_available_test.py  |  5 -----
 4 files changed, 8 insertions(+), 24 deletions(-)
Documentation/process/changes.rst +1 -3
index 5685d7bfe4d0..0d0b7120792b 100644
@@ -88,9 +88,7 @@ docs on :ref:`Building Linux with Clang/LLVM <kbuild_llvm>`.
 Rust (optional)
 ---------------
 
-A particular version of the Rust toolchain is required. Newer versions may or
-may not work because the kernel depends on some unstable Rust features, for
-the moment.
+A recent version of the Rust compiler is required.
 
 Each Rust toolchain comes with several "components", some of which are required
 (like ``rustc``) and some that are optional. The ``rust-src`` component (which
Documentation/rust/quick-start.rst +7 -8
index ac2f16288458..89bbfde8c96c 100644
@@ -36,16 +36,15 @@ if that is the case.
 rustc
 *****
 
-A particular version of the Rust compiler is required. Newer versions may or
-may not work because, for the moment, the kernel depends on some unstable
-Rust features.
+A recent version of the Rust compiler is required.
 
 If ``rustup`` is being used, enter the kernel build directory (or use
-``--path=<build-dir>`` argument to the ``set`` sub-command) and run::
+``--path=<build-dir>`` argument to the ``set`` sub-command) and run,
+for instance::
 
-	rustup override set $(scripts/min-tool-version.sh rustc)
+	rustup override set stable
 
-This will configure your working directory to use the correct version of
+This will configure your working directory to use the given version of
 ``rustc`` without affecting your default toolchain.
 
 Note that the override applies to the current working directory (and its
@@ -72,9 +71,9 @@ version later on requires re-adding the component.
 Otherwise, if a standalone installer is used, the Rust source tree may be
 downloaded into the toolchain's installation folder::
 
-	curl -L "https://static.rust-lang.org/dist/rust-src-$(scripts/min-tool-version.sh rustc).tar.gz" |
+	curl -L "https://static.rust-lang.org/dist/rust-src-$(rustc --version | cut -d' ' -f2).tar.gz" |
 		tar -xzf - -C "$(rustc --print sysroot)/lib" \
-		"rust-src-$(scripts/min-tool-version.sh rustc)/rust-src/lib/" \
+		"rust-src-$(rustc --version | cut -d' ' -f2)/rust-src/lib/" \
 		--strip-components=3
 
 In this case, upgrading the Rust compiler version later on requires manually
scripts/rust_is_available.sh +0 -8
index 117018946b57..67cb900124cc 100755
@@ -117,14 +117,6 @@ if [ "$rust_compiler_cversion" -lt "$rust_compiler_min_cversion" ]; then
 	echo >&2 "***"
 	exit 1
 fi
-if [ "$rust_compiler_cversion" -gt "$rust_compiler_min_cversion" ]; then
-	echo >&2 "***"
-	echo >&2 "*** Rust compiler '$RUSTC' is too new. This may or may not work."
-	echo >&2 "***   Your version:     $rust_compiler_version"
-	echo >&2 "***   Expected version: $rust_compiler_min_version"
-	echo >&2 "***"
-	warning=1
-fi
 
 # Check that the Rust bindings generator is suitable.
 #
scripts/rust_is_available_test.py +0 -5
index 57613fe5ed75..a255f79aafc2 100755
@@ -193,11 +193,6 @@ else:
         result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc })
         self.assertIn(f"Rust compiler '{rustc}' is too old.", result.stderr)
 
-    def test_rustc_new_version(self):
-        rustc = self.generate_rustc("rustc 1.999.0 (a8314ef7d 2099-06-27)")
-        result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "RUSTC": rustc })
-        self.assertIn(f"Rust compiler '{rustc}' is too new. This may or may not work.", result.stderr)
-
     def test_bindgen_nonexecutable(self):
         result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.nonexecutable })
         self.assertIn(f"Running '{self.nonexecutable}' to check the Rust bindings generator version failed with", result.stderr)

--
2.45.2

author maintainer
#7 lore raw

PATCH v2 7/13 rust: avoid assuming a particular `bindgen` build

In reply to Miguel Ojeda

bindgen's logic to find libclang (via clang-sys) may change over time, and depends on how it was built (e.g. Linux distributions may decide to build it differently, and we are going to provide documentation on installing it via distributions later in this series).

Therefore, clarify that bindgen may be built in several ways and simplify the documentation by only mentioning the most prominent environment variable (LIBCLANG_PATH) as an example on how to tweak the search of the library at runtime (i.e. when bindgen is built as our documentation explains). This also avoids duplicating the documentation, like bindgen itself does (i.e. it refers to clang-sys).

Similarly, replace the test we had for this (which used the real program) with a mocked one, to avoid depending on the particular build as well.

---
 Documentation/rust/quick-start.rst | 23 +++++++++--------------
 scripts/rust_is_available_test.py  | 25 +++++++++++++++----------
 2 files changed, 24 insertions(+), 24 deletions(-)
Documentation/rust/quick-start.rst +9 -14
index 89bbfde8c96c..5ea8c8914942 100644
@@ -113,20 +113,15 @@ Install it via (note that this will download and build the tool from source)::
 
 	cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
 
-``bindgen`` needs to find a suitable ``libclang`` in order to work. If it is
-not found (or a different ``libclang`` than the one found should be used),
-the process can be tweaked using the environment variables understood by
-``clang-sys`` (the Rust bindings crate that ``bindgen`` uses to access
-``libclang``):
-
-* ``LLVM_CONFIG_PATH`` can be pointed to an ``llvm-config`` executable.
-
-* Or ``LIBCLANG_PATH`` can be pointed to a ``libclang`` shared library
-  or to the directory containing it.
-
-* Or ``CLANG_PATH`` can be pointed to a ``clang`` executable.
-
-For details, please see ``clang-sys``'s documentation at:
+``bindgen`` uses the ``clang-sys`` crate to find a suitable ``libclang`` (which
+may be linked statically, dynamically or loaded at runtime). By default, the
+``cargo`` command above will produce a ``bindgen`` binary that will load
+``libclang`` at runtime. If it is not found (or a different ``libclang`` than
+the one found should be used), the process can be tweaked, e.g. by using the
+``LIBCLANG_PATH`` environment variable. For details, please see ``clang-sys``'s
+documentation at:
+
+	https://github.com/KyleMayes/clang-sys#linking
 
 	https://github.com/KyleMayes/clang-sys#environment-variables
 
scripts/rust_is_available_test.py +15 -10
index a255f79aafc2..0481aab862ec 100755
@@ -55,10 +55,15 @@ else:
 
     @classmethod
     def generate_bindgen(cls, version_stdout, libclang_stderr):
+        if libclang_stderr is None:
+            libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
+        else:
+            libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)"
+
         return cls.generate_executable(f"""#!/usr/bin/env python3
 import sys
 if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
-    print({repr(libclang_stderr)}, file=sys.stderr)
+    {libclang_case}
 else:
     print({repr(version_stdout)})
 """)
@@ -67,6 +72,10 @@ else:
     def generate_bindgen_version(cls, stdout):
         return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr)
 
+    @classmethod
+    def generate_bindgen_libclang_failure(cls):
+        return cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, None)
+
     @classmethod
     def generate_bindgen_libclang(cls, stderr):
         return cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, stderr)
@@ -89,6 +98,7 @@ else:
         cls.rust_default_sysroot = subprocess.check_output(("rustc", "--print", "sysroot")).decode().strip()
 
         cls.bindgen_default_bindgen_version_stdout = f"bindgen {cls.bindgen_default_version}"
+        cls.bindgen_default_bindgen_libclang_failure_exit_code = 42
         cls.bindgen_default_bindgen_libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {cls.llvm_default_version} [-W#pragma-messages], err: false"
 
         cls.default_rustc = cls.generate_rustc(f"rustc {cls.rustc_default_version}")
@@ -227,15 +237,10 @@ else:
         self.assertIn(f"Rust bindings generator '{bindgen}' is too new. This may or may not work.", result.stderr)
 
     def test_bindgen_libclang_failure(self):
-        for env in (
-            { "LLVM_CONFIG_PATH": self.missing },
-            { "LIBCLANG_PATH": self.missing },
-            { "CLANG_PATH": self.missing },
-        ):
-            with self.subTest(env=env):
-                result = self.run_script(self.Expected.FAILURE, env | { "PATH": os.environ["PATH"], "BINDGEN": "bindgen" })
-                self.assertIn("Running 'bindgen' to check the libclang version (used by the Rust", result.stderr)
-                self.assertIn("bindings generator) failed with code ", result.stderr)
+        bindgen = self.generate_bindgen_libclang_failure()
+        result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
+        self.assertIn(f"Running '{bindgen}' to check the libclang version (used by the Rust", result.stderr)
+        self.assertIn(f"bindings generator) failed with code {self.bindgen_default_bindgen_libclang_failure_exit_code}. This may be caused by", result.stderr)
 
     def test_bindgen_libclang_unexpected_version(self):
         bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version unexpected [-W#pragma-messages], err: false")
Signature · 1 line
2.45.2
author maintainer
#8 lore raw

PATCH v2 8/13 rust: work around `bindgen` 0.69.0 issue

In reply to Miguel Ojeda

bindgen 0.69.0 contains a bug: --version does not work without providing a header [1]:

error: the following required arguments were not provided:
<HEADER>

Usage: bindgen <FLAGS> <OPTIONS> <HEADER> -- <CLANG_ARGS>...

Thus, in preparation for supporting several bindgen versions, work around the issue by passing a dummy argument.

Include a comment so that we can remove the workaround in the future.

---
 init/Kconfig                 | 5 ++++-
 scripts/rust_is_available.sh | 6 +++++-
 2 files changed, 9 insertions(+), 2 deletions(-)
init/Kconfig +4 -1
index febdea2afc3b..94e20d3b99d4 100644
@@ -1928,7 +1928,10 @@ config RUSTC_VERSION_TEXT
 config BINDGEN_VERSION_TEXT
 	string
 	depends on RUST
-	default $(shell,command -v $(BINDGEN) >/dev/null 2>&1 && $(BINDGEN) --version || echo n)
+	# The dummy parameter `workaround-for-0.69.0` is required to support 0.69.0
+	# (https://github.com/rust-lang/rust-bindgen/pull/2678). It can be removed when
+	# the minimum version is upgraded past that (0.69.1 already fixed the issue).
+	default $(shell,command -v $(BINDGEN) >/dev/null 2>&1 && $(BINDGEN) --version workaround-for-0.69.0 || echo n)
 
 #
 # Place an empty function call at each tracepoint site. Can be
scripts/rust_is_available.sh +5 -1
index 67cb900124cc..1881e8f2a2b9 100755
@@ -121,8 +121,12 @@ fi
 # Check that the Rust bindings generator is suitable.
 #
 # Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
+#
+# The dummy parameter `workaround-for-0.69.0` is required to support 0.69.0
+# (https://github.com/rust-lang/rust-bindgen/pull/2678). It can be removed when
+# the minimum version is upgraded past that (0.69.1 already fixed the issue).
 rust_bindings_generator_output=$( \
-	LC_ALL=C "$BINDGEN" --version 2>/dev/null
+	LC_ALL=C "$BINDGEN" --version workaround-for-0.69.0 2>/dev/null
 ) || rust_bindings_generator_code=$?
 if [ -n "$rust_bindings_generator_code" ]; then
 	echo >&2 "***"
Signature · 1 line
2.45.2
author maintainer
#9 lore raw

PATCH v2 9/13 rust: start supporting several `bindgen` versions

In reply to Miguel Ojeda

With both the workaround for bindgen 0.69.0 and the warning about 0.66.0 and 0.66.1 in place, start supporting several bindgen versions, like it was done for the Rust compiler in a previous patch.

All other versions, including the latest 0.69.4, build without errors.

The bindgen project, like Rust, has also agreed to have the kernel in their CI [1] -- thanks! This should help both projects: bindgen will be able to detect early issues like those mentioned above, and the kernel will be very likely build with new releases (at least for the basic configuration being tested).

---
v2:
- Update suggested cargo command to just install the latest, i.e. removing the --version flag, following the same pattern as Finn's suggestion to use stable for the rustup command.

 Documentation/rust/quick-start.rst | 7 ++++---
 scripts/rust_is_available.sh       | 8 --------
 scripts/rust_is_available_test.py  | 5 -----
 3 files changed, 4 insertions(+), 16 deletions(-)
Documentation/rust/quick-start.rst +4 -3
index 5ea8c8914942..66cefbab8f9a 100644
@@ -107,11 +107,12 @@ bindgen
 *******
 
 The bindings to the C side of the kernel are generated at build time using
-the ``bindgen`` tool. A particular version is required.
+the ``bindgen`` tool.
 
-Install it via (note that this will download and build the tool from source)::
+Install it, for instance, via (note that this will download and build the tool
+from source)::
 
-	cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
+	cargo install --locked bindgen-cli
 
 ``bindgen`` uses the ``clang-sys`` crate to find a suitable ``libclang`` (which
 may be linked statically, dynamically or loaded at runtime). By default, the
scripts/rust_is_available.sh +0 -8
index 1881e8f2a2b9..4531f9dd19d3 100755
@@ -161,14 +161,6 @@ if [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cvers
 	echo >&2 "***"
 	exit 1
 fi
-if [ "$rust_bindings_generator_cversion" -gt "$rust_bindings_generator_min_cversion" ]; then
-	echo >&2 "***"
-	echo >&2 "*** Rust bindings generator '$BINDGEN' is too new. This may or may not work."
-	echo >&2 "***   Your version:     $rust_bindings_generator_version"
-	echo >&2 "***   Expected version: $rust_bindings_generator_min_version"
-	echo >&2 "***"
-	warning=1
-fi
 
 # Check that the `libclang` used by the Rust bindings generator is suitable.
 #
scripts/rust_is_available_test.py +0 -5
index 0481aab862ec..d6d54b7ea42a 100755
@@ -231,11 +231,6 @@ else:
         result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
         self.assertIn(f"Rust bindings generator '{bindgen}' is too old.", result.stderr)
 
-    def test_bindgen_new_version(self):
-        bindgen = self.generate_bindgen_version("bindgen 0.999.0")
-        result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
-        self.assertIn(f"Rust bindings generator '{bindgen}' is too new. This may or may not work.", result.stderr)
-
     def test_bindgen_libclang_failure(self):
         bindgen = self.generate_bindgen_libclang_failure()
         result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })

--
2.45.2

author maintainer
#10 lore raw

PATCH v2 10/13 rust: warn about `bindgen` versions 0.66.0 and 0.66.1

In reply to Miguel Ojeda

bindgen versions 0.66.0 and 0.66.1 panic due to C string literals with NUL characters [1]:

    panicked at .cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-0.66.0/codegen/mod.rs:717:71:
    called `Result::unwrap()` on an `Err` value: FromBytesWithNulError { kind: InteriorNul(4) }

Thus, in preparation for supporting several bindgen versions, add a version check to warn the user about it.

Since some distributions may have patched it (e.g. Debian did [2]), check if that seems to be the case (after the version check matches), in order to avoid printing a warning in that case.

We could make it an error, but 1) it is going to fail anyway later in the build, 2) we would disable RUST, which is also painful, 3) someone could have patched it in a way that still makes our extra check fail (however unlikely), 4) the interior NUL may go away in the headers (however unlikely). Thus just warn about it so that users know why it is failing.

In addition, add a couple tests for the new cases.

---
v2:
- If the version matches, then also check whether the bindgen binary fails with a simple interior NUL case, so that we can avoid the warning if it has been patched (like Debian's). Update the comment for the warning as well, and add a test for it too.
- Move test later in the series so that the new test does not fail due to the "too new" check.
- Split if condition in 2 lines.

 scripts/rust_is_available.sh             | 13 ++++++++++++
 scripts/rust_is_available_bindgen_0_66.h |  2 ++
 scripts/rust_is_available_test.py        | 26 +++++++++++++++++++++---
 3 files changed, 38 insertions(+), 3 deletions(-)
 create mode 100644 scripts/rust_is_available_bindgen_0_66.h
scripts/rust_is_available.sh +13 -0
index 4531f9dd19d3..5262c56dd674 100755
@@ -161,6 +161,19 @@ if [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cvers
 	echo >&2 "***"
 	exit 1
 fi
+if [ "$rust_bindings_generator_cversion" -eq 6600 ] ||
+	[ "$rust_bindings_generator_cversion" -eq 6601 ]; then
+	# Distributions may have patched the issue (e.g. Debian did).
+	if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_0_66.h >/dev/null; then
+		echo >&2 "***"
+		echo >&2 "*** Rust bindings generator '$BINDGEN' versions 0.66.0 and 0.66.1 may not"
+		echo >&2 "*** work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/2567),"
+		echo >&2 "*** unless patched (like Debian's)."
+		echo >&2 "***   Your version:     $rust_bindings_generator_version"
+		echo >&2 "***"
+		warning=1
+	fi
+fi
 
 # Check that the `libclang` used by the Rust bindings generator is suitable.
 #
scripts/rust_is_available_bindgen_0_66.h new file +2 -0
new file mode 100644
index 000000000000..c0431293421c
@@ -0,0 +1,2 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#define A "\0"
scripts/rust_is_available_test.py +23 -3
index d6d54b7ea42a..413741037fb3 100755
@@ -54,23 +54,30 @@ else:
 """)
 
     @classmethod
-    def generate_bindgen(cls, version_stdout, libclang_stderr):
+    def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False):
         if libclang_stderr is None:
             libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
         else:
             libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)"
 
+        if version_0_66_patched:
+            version_0_66_case = "pass"
+        else:
+            version_0_66_case = "raise SystemExit(1)"
+
         return cls.generate_executable(f"""#!/usr/bin/env python3
 import sys
 if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
     {libclang_case}
+elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv):
+    {version_0_66_case}
 else:
     print({repr(version_stdout)})
 """)
 
     @classmethod
-    def generate_bindgen_version(cls, stdout):
-        return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr)
+    def generate_bindgen_version(cls, stdout, version_0_66_patched=False):
+        return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr, version_0_66_patched)
 
     @classmethod
     def generate_bindgen_libclang_failure(cls):
@@ -231,6 +238,19 @@ else:
         result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
         self.assertIn(f"Rust bindings generator '{bindgen}' is too old.", result.stderr)
 
+    def test_bindgen_bad_version_0_66_0_and_0_66_1(self):
+        for version in ("0.66.0", "0.66.1"):
+            with self.subTest(version=version):
+                bindgen = self.generate_bindgen_version(f"bindgen {version}")
+                result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
+                self.assertIn(f"Rust bindings generator '{bindgen}' versions 0.66.0 and 0.66.1 may not", result.stderr)
+
+    def test_bindgen_bad_version_0_66_0_and_0_66_1_patched(self):
+        for version in ("0.66.0", "0.66.1"):
+            with self.subTest(version=version):
+                bindgen = self.generate_bindgen_version(f"bindgen {version}", True)
+                result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen })
+
     def test_bindgen_libclang_failure(self):
         bindgen = self.generate_bindgen_libclang_failure()
         result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })

--
2.45.2

author maintainer
#11 lore raw

PATCH v2 11/13 kbuild: rust: add `rustc-version` support

In reply to Miguel Ojeda

Now that we are starting to support several Rust versions, introduce rustc-version support, mimicking the C side:

- scripts/rustc-version.sh, that mimics the other version scripts

     (with one more digit, e.g. Rust 1.79.0 is 107900).

- rustc-{info,name,version} Kbuild macros.

- CONFIG_RUSTC_VERSION Kconfig symbol that calls rustc-version.

- rustc-min-version Kbuild macro that uses CONFIG_RUSTC_VERSION.

With these, we can easily support flags conditionally depending on rustc's version -- a user comes in the next patch.

Another user will be the -Ctarget-feature=+reserve-x18/-Zfixed-x18 arm64 flags [1].

---
v2:
- Fix comment from "5 or 6-digit" to "6 to 7-digit".

 init/Kconfig              |  6 +++++
 scripts/Kconfig.include   |  6 +++++
 scripts/Makefile.compiler |  4 +++
 scripts/rustc-version.sh  | 52 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 68 insertions(+)
 create mode 100755 scripts/rustc-version.sh
init/Kconfig +6 -0
index 94e20d3b99d4..7d344f248785 100644
@@ -1920,6 +1920,12 @@ config RUST
 
 	  If unsure, say N.
 
+config RUSTC_VERSION
+	int
+	depends on RUST
+	default $(rustc-version)
+	default 0
+
 config RUSTC_VERSION_TEXT
 	string
 	depends on RUST
scripts/Kconfig.include +6 -0
index 3ee8ecfb8c04..82ab889725db 100644
@@ -45,6 +45,12 @@ $(error-if,$(success,test -z "$(cc-info)"),Sorry$(comma) this C compiler is not
 cc-name := $(shell,set -- $(cc-info) && echo $1)
 cc-version := $(shell,set -- $(cc-info) && echo $2)
 
+# Get the Rust compiler name, version, and error out if it is not supported.
+rustc-info := $(shell,$(srctree)/scripts/rustc-version.sh $(RUSTC))
+$(error-if,$(success,test -z "$(rustc-info)"),Sorry$(comma) this Rust compiler is not supported.)
+rustc-name := $(shell,set -- $(rustc-info) && echo $1)
+rustc-version := $(shell,set -- $(rustc-info) && echo $2)
+
 # Get the assembler name, version, and error out if it is not supported.
 as-info := $(shell,$(srctree)/scripts/as-version.sh $(CC) $(CLANG_FLAGS))
 $(error-if,$(success,test -z "$(as-info)"),Sorry$(comma) this assembler is not supported.)
scripts/Makefile.compiler +4 -0
index 92be0c9a13ee..17eaa085b59c 100644
@@ -69,6 +69,10 @@ gcc-min-version = $(call test-ge, $(CONFIG_GCC_VERSION), $1)
 # Usage: cflags-$(call clang-min-version, 110000) += -foo
 clang-min-version = $(call test-ge, $(CONFIG_CLANG_VERSION), $1)
 
+# rustc-min-version
+# Usage: rustflags-$(call rustc-min-version, 107900) += -foo
+rustc-min-version = $(call test-ge, $(CONFIG_RUSTC_VERSION), $1)
+
 # ld-option
 # Usage: KBUILD_LDFLAGS += $(call ld-option, -X, -Y)
 ld-option = $(call try-run, $(LD) $(KBUILD_LDFLAGS) $(1) -v,$(1),$(2),$(3))
scripts/rustc-version.sh new file +52 -0

52 lines not on this page · raw message

--
2.45.2

#12 lore raw

Re: [PATCH v2 11/13] kbuild: rust: add `rustc-version` support

Quoted 2 lines · Miguel Ojeda

+$(error-if,$(success,test -z "$(rustc-info)"),Sorry$(comma) this Rust compiler is not supported.)

Bah, this is broken, I just noticed in my CI that I didn't handle the "Rust not installed" case.

Anyway, this patch (and the next one) are not important for the series, I will just drop them and send them independently next cycle to Kbuild instead. I should have done that anyway, even if they were correct.

Cheers,
Miguel

author maintainer
#13 lore raw

PATCH v2 12/13 rust: support the new `-Zub-checks` flag

In reply to Miguel Ojeda

Rust 1.79.0 has introduced a new codegen flag, -Zub-checks [1], to allow to independently configure (from -Cdebug-assertions) whether the extra runtime checks for UB are emitted, in a similar fashion to -Coverflow-checks.

This allows to configure the kernel with only the UB checks enabled, but not the debug_assert!s; or vice versa, e.g. [2].

It also showcases how RUSTC_VERSION and the Kbuild macros, introduced in the previous commit, can be used.

---
 Makefile          |  9 +++++++--
 lib/Kconfig.debug | 18 ++++++++++++++++++
 2 files changed, 25 insertions(+), 2 deletions(-)
Makefile +7 -2
index 9044fdb9adb1..4cf3b9799ec9 100644
@@ -821,10 +821,15 @@ KBUILD_CFLAGS += -Os
 KBUILD_RUSTFLAGS += -Copt-level=s
 endif
 
-# Always set `debug-assertions` and `overflow-checks` because their default
-# depends on `opt-level` and `debug-assertions`, respectively.
+# Always set `debug-assertions` because its default depends on `opt-level`.
 KBUILD_RUSTFLAGS += -Cdebug-assertions=$(if $(CONFIG_RUST_DEBUG_ASSERTIONS),y,n)
+
+# Always set `overflow-checks` and `ub-checks` because their default depends on
+# `debug-assertions`.
 KBUILD_RUSTFLAGS += -Coverflow-checks=$(if $(CONFIG_RUST_OVERFLOW_CHECKS),y,n)
+ifeq ($(call rustc-min-version, 107900),y)
+KBUILD_RUSTFLAGS += -Zub-checks=$(if $(CONFIG_RUST_UNDEFINED_BEHAVIOR_CHECKS),y,n)
+endif
 
 # Tell gcc to never replace conditional load with a non-conditional one
 ifdef CONFIG_CC_IS_GCC
lib/Kconfig.debug +18 -0
index 59b6765d86b8..6b4f512f9e13 100644
@@ -3020,6 +3020,24 @@ config RUST_OVERFLOW_CHECKS
 
 	  If unsure, say Y.
 
+config RUST_UNDEFINED_BEHAVIOR_CHECKS
+	bool "Undefined Behavior checks"
+	depends on RUST && RUSTC_VERSION >= 107900
+	help
+	  Enables rustc's `-Zub-checks` codegen option.
+
+	  This flag allows you to control whether additional runtime checks that
+	  detect some causes of Undefined Behavior at runtime will be emitted.
+	  When enabled, a Rust panic will occur if UB is detected.
+
+	  All checks are generated on a best-effort basis; even if there is a check
+	  implemented for some cause of Undefined Behavior, it may be possible for
+	  the check to not fire.
+
+	  Note that this will apply to all Rust code, including `core`.
+
+	  If unsure, say N.
+
 config RUST_BUILD_ASSERT_ALLOW
 	bool "Allow unoptimized build-time assertions"
 	depends on RUST
Signature · 1 line
2.45.2
author maintainer
#14 lore raw

PATCH v2 13/13 docs: rust: quick-start: add section on Linux distributions

In reply to Miguel Ojeda

Now that we are starting to support several Rust compiler and bindgen versions, there is a good chance some Linux distributions work out of the box.

Thus, provide some instructions on how to set the toolchain up for a few major Linux distributions. This simplifies the setup users need to build the kernel.

In addition, add an introduction to the document so that it is easier to understand its structure and move the LLVM+Rust kernel.org toolchains paragraph there (removing "depending on the Linux version"). We may want to reorganize the document or split it in the future, but I wanted to focus this commit on the new information added about each particular distribution.

Finally, remove the rustup's components mention in changes.rst since users do not need it if they install the toolchain via the distributions (and anyway it was too detailed for that main document).

Cc Jan Alexander Steffens <[email protected]>
Cc Johannes Löthberg <[email protected]>
Cc Fabian Grünbichler <[email protected]>
Cc Josh Stone <[email protected]>
Cc Randy Barlow <[email protected]>
Cc Anna (navi) Figueiredo Gomes <[email protected]>
Cc Matoro Mahri <[email protected]>
Cc Ryan Scheel <[email protected]>
Cc figsoda <[email protected]>
Cc Jörg Thalheim <[email protected]>
Cc Theodore Ni <[email protected]>
Cc Winter <[email protected]>
Cc William Brown <[email protected]>
Cc Xiaoguang Wang <[email protected]>
Cc Andrea Righi <[email protected]>
Cc Zixing Liu <[email protected]>
Cc Nathan Chancellor <[email protected]>

---
v2:
- Add openSUSE Slowroll.
- Update openSUSE docs: both Slowroll and Tumbleweed now provide rustfmt, Clippy and the Rust standard library sources, so the comment about those can be dropped. (William)
- I ended up leaving the Debian/Ubuntu docs as-is for the time being. Happy to get a patch for that later. We should definitely add them if they start working for the current kernel (currently: Ubuntu's latest versioned package is 1.76, and Debian's web one is 1.70). (Andrea, Fabian)
- Move the LLVM+Rust kernel.org toolchains paragraph to the introduction that this commit adds, now that we have access to that paragraph since the series is rebased on top of rust-next.
- Use plural for "release".

 Documentation/process/changes.rst  |  5 --
 Documentation/rust/quick-start.rst | 93 ++++++++++++++++++++++++++----
 2 files changed, 81 insertions(+), 17 deletions(-)
Documentation/process/changes.rst +0 -5
index 0d0b7120792b..0ce96ae2588c 100644
@@ -90,11 +90,6 @@ Rust (optional)
 
 A recent version of the Rust compiler is required.
 
-Each Rust toolchain comes with several "components", some of which are required
-(like ``rustc``) and some that are optional. The ``rust-src`` component (which
-is optional) needs to be installed to build the kernel. Other components are
-useful for developing.
-
 Please see Documentation/rust/quick-start.rst for instructions on how to
 satisfy the build requirements of Rust support. In particular, the ``Makefile``
 target ``rustavailable`` is useful to check why the Rust toolchain may not
Documentation/rust/quick-start.rst +81 -12

105 lines not on this page · raw message

--
2.45.2

Applied
#15 lore raw
In reply to Miguel Ojeda
Quoted 5 lines · Miguel Ojeda

A few things improved here and there, and rebased on top of rust-next.

The changelog is attached to each patch.

Quoted 7 lines

I kept the Tested-bys since most of the changes are on documentation or comments, though I did remove them on the patch that changed the most just in case (even for that one, I think Benno's and Andreas' setup would not have made a difference).

I plan to put this series into rust-next very soon so that it goes into the merge window.

Applied into rust-next -- thanks everyone!

Cheers,
Miguel