Re: [PATCH 15/19] Kbuild: add Rust support

From: Nick Desaulniers
Date: Tue Dec 07 2021 - 17:45:23 EST


"(/.On Mon, Dec 6, 2021 at 6:07 AM Miguel Ojeda <ojeda@xxxxxxxxxx> wrote:
>

... snip ...

Miguel and team,
I'm happy to see progress here! Just some quick notes after a first pass.

Testing this series applied on mainline, I see:
$ make LLVM=1 -j72 defconfig
$ grep RUST .config
CONFIG_HAS_RUST=y
CONFIG_RUSTC_VERSION=14000
# CONFIG_RUST is not set
...
$ make LLVM=1 -j72 menuconfig
# enable CONFIG_RUST
$ make LLVM=1
...
CALL scripts/checksyscalls.sh
error: @path is unstable - use -Z unstable-options to enable its use

error: @path is unstable - use -Z unstable-options to enable its use
$ rustc --version
rustc 1.40.0 (73528e339 2019-12-16)

Can we update some Kconfig checks to fix that?

> --- a/Makefile
> +++ b/Makefile
> @@ -120,6 +120,13 @@ endif
>
> export KBUILD_CHECKSRC
>
> +# Enable "clippy" (a linter) as part of the Rust compilation.
> +#
> +# Use 'make CLIPPY=1' to enable it.
> +ifeq ("$(origin CLIPPY)", "command line")
> + KBUILD_CLIPPY := $(CLIPPY)
> +endif
> +

KBUILD_CLIPPY isn't used until ...

> # Use make M=dir or set the environment variable KBUILD_EXTMOD to specify the
> # directory of external module to build. Setting M= takes precedence.
> ifeq ("$(origin M)", "command line")
> @@ -267,7 +274,7 @@ no-dot-config-targets := $(clean-targets) \
> cscope gtags TAGS tags help% %docs check% coccicheck \
> $(version_h) headers headers_% archheaders archscripts \
> %asm-generic kernelversion %src-pkg dt_binding_check \
> - outputmakefile
> + outputmakefile rustfmt rustfmtcheck
> # Installation targets should not require compiler. Unfortunately, vdso_install
> # is an exception where build artifacts may be updated. This must be fixed.
> no-compiler-targets := $(no-dot-config-targets) install dtbs_install \
> @@ -461,6 +468,12 @@ OBJDUMP = $(CROSS_COMPILE)objdump
> READELF = $(CROSS_COMPILE)readelf
> STRIP = $(CROSS_COMPILE)strip
> endif
> +RUSTC = rustc
> +RUSTDOC = rustdoc
> +RUSTFMT = rustfmt
> +CLIPPY_DRIVER = clippy-driver
> +BINDGEN = bindgen
> +CARGO = cargo
> PAHOLE = pahole
> RESOLVE_BTFIDS = $(objtree)/tools/bpf/resolve_btfids/resolve_btfids
> LEX = flex
> @@ -486,9 +499,11 @@ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
> -Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF)
> NOSTDINC_FLAGS :=
> CFLAGS_MODULE =
> +RUSTFLAGS_MODULE =
> AFLAGS_MODULE =
> LDFLAGS_MODULE =
> CFLAGS_KERNEL =
> +RUSTFLAGS_KERNEL =
> AFLAGS_KERNEL =
> LDFLAGS_vmlinux =
>
> @@ -517,15 +532,46 @@ KBUILD_CFLAGS := -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs \
> -Werror=return-type -Wno-format-security \
> -std=gnu89
> KBUILD_CPPFLAGS := -D__KERNEL__
> +KBUILD_RUST_TARGET := $(srctree)/arch/$(SRCARCH)/rust/target.json
> +KBUILD_RUSTFLAGS := --emit=dep-info,obj,metadata --edition=2021 \
> + -Cpanic=abort -Cembed-bitcode=n -Clto=n -Crpath=n \
> + -Cforce-unwind-tables=n -Ccodegen-units=1 \
> + -Zbinary_dep_depinfo=y -Zsymbol-mangling-version=v0 \
> + -Dunsafe_op_in_unsafe_fn -Drust_2018_idioms \
> + -Dunreachable_pub -Dnon_ascii_idents \
> + -Wmissing_docs \
> + -Drustdoc::missing_crate_level_docs \
> + -Dclippy::correctness -Dclippy::style -Dclippy::suspicious \
> + -Dclippy::complexity -Dclippy::perf -Dclippy::float_arithmetic \
> + -Dclippy::let_unit_value -Dclippy::mut_mut \
> + -Dclippy::needless_bitwise_bool -Dclippy::needless_continue \
> + -Wclippy::dbg_macro

Consider putting clippy specific flags in their own variable, and/or
just adding them to KBUILD_RUSTFLAGS only when clippy is actually
being used.

> KBUILD_AFLAGS_KERNEL :=
> KBUILD_CFLAGS_KERNEL :=
> +KBUILD_RUSTFLAGS_KERNEL :=
> KBUILD_AFLAGS_MODULE := -DMODULE
> KBUILD_CFLAGS_MODULE := -DMODULE
> +KBUILD_RUSTFLAGS_MODULE := --cfg MODULE
> KBUILD_LDFLAGS_MODULE :=
> KBUILD_LDFLAGS :=
> CLANG_FLAGS :=
>
> +ifeq ($(KBUILD_CLIPPY),1)
> + RUSTC_OR_CLIPPY_QUIET := CLIPPY
> + RUSTC_OR_CLIPPY = $(CLIPPY_DRIVER)
> +else
> + RUSTC_OR_CLIPPY_QUIET := RUSTC
> + RUSTC_OR_CLIPPY = $(RUSTC)
> +endif

... here. Should we sink the check for origin CLIPPY? Can we also
match how we check for LLVM=1 rather than checking the variables
origin? Especially since KBUILD_CLIPPY isn't exported, it seems it's
not used outside of this file, IIUC?

> +
> +ifdef RUST_LIB_SRC
> + export RUST_LIB_SRC
> +endif
> +
> +export RUSTC_BOOTSTRAP := 1

Is this used in a different patch in the series? ^

> +
> export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
> +export RUSTC RUSTDOC RUSTFMT RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY BINDGEN CARGO
> export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL
> export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
> export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD
> @@ -533,9 +579,10 @@ export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE
>
> export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS
> export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
> +export KBUILD_RUST_TARGET KBUILD_RUSTFLAGS RUSTFLAGS_KERNEL RUSTFLAGS_MODULE
> export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
> -export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
> -export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
> +export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_RUSTFLAGS_MODULE KBUILD_LDFLAGS_MODULE
> +export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL KBUILD_RUSTFLAGS_KERNEL
> export PAHOLE_FLAGS
>
> # Files to ignore in find ... statements
> @@ -727,7 +774,7 @@ $(KCONFIG_CONFIG):
> #
> # Do not use $(call cmd,...) here. That would suppress prompts from syncconfig,
> # so you cannot notice that Kconfig is waiting for the user input.
> -%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h: $(KCONFIG_CONFIG)
> +%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h %/generated/rustc_cfg: $(KCONFIG_CONFIG)
> $(Q)$(kecho) " SYNC $@"
> $(Q)$(MAKE) -f $(srctree)/Makefile syncconfig
> else # !may-sync-config
> @@ -756,11 +803,27 @@ KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member)
>
> ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
> KBUILD_CFLAGS += -O2
> +KBUILD_RUSTFLAGS_OPT_LEVEL_MAP := 2
> else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
> KBUILD_CFLAGS += -O3
> +KBUILD_RUSTFLAGS_OPT_LEVEL_MAP := 3
> else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
> KBUILD_CFLAGS += -Os
> -endif
> +KBUILD_RUSTFLAGS_OPT_LEVEL_MAP := z

If z and s are distinct opt levels, then rustc should really be using
s rather than z. IME, z has created larger images than s due to LLVM
aggressively emitting libcalls, regardless of the cost of call setup.
See also commit a75bb4eb9e56 ("Revert "kbuild: use -Oz instead of -Os
when using clang")

> +endif
> +
> +# Always set `debug-assertions` and `overflow-checks` because their default
> +# depends on `opt-level` and `debug-assertions`, respectively.
> +KBUILD_RUSTFLAGS += -Cdebug-assertions=$(if $(CONFIG_RUST_DEBUG_ASSERTIONS),y,n)
> +KBUILD_RUSTFLAGS += -Coverflow-checks=$(if $(CONFIG_RUST_OVERFLOW_CHECKS),y,n)
> +KBUILD_RUSTFLAGS += -Copt-level=$\
> + $(if $(CONFIG_RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C),$(KBUILD_RUSTFLAGS_OPT_LEVEL_MAP))$\
> + $(if $(CONFIG_RUST_OPT_LEVEL_0),0)$\
> + $(if $(CONFIG_RUST_OPT_LEVEL_1),1)$\
> + $(if $(CONFIG_RUST_OPT_LEVEL_2),2)$\
> + $(if $(CONFIG_RUST_OPT_LEVEL_3),3)$\
> + $(if $(CONFIG_RUST_OPT_LEVEL_S),s)$\
> + $(if $(CONFIG_RUST_OPT_LEVEL_Z),z)

I'm still not a fan of this separate configurability for optimization
level differing from C.

>
> # Tell gcc to never replace conditional load with a non-conditional one
> ifdef CONFIG_CC_IS_GCC
> @@ -791,6 +854,9 @@ KBUILD_CFLAGS += $(stackp-flags-y)
> KBUILD_CFLAGS-$(CONFIG_WERROR) += -Werror
> KBUILD_CFLAGS += $(KBUILD_CFLAGS-y) $(CONFIG_CC_IMPLICIT_FALLTHROUGH)
>
> +KBUILD_RUSTFLAGS-$(CONFIG_WERROR) += -Dwarnings
> +KBUILD_RUSTFLAGS += $(KBUILD_RUSTFLAGS-y)
> +
> ifdef CONFIG_CC_IS_CLANG
> KBUILD_CPPFLAGS += -Qunused-arguments
> # The kernel builds with '-std=gnu89' so use of GNU extensions is acceptable.
> @@ -812,6 +878,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, unused-const-variable)
>
> ifdef CONFIG_FRAME_POINTER
> KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
> +KBUILD_RUSTFLAGS += -Cforce-frame-pointers=y
> else
> # Some targets (ARM with Thumb2, for example), can't be built with frame
> # pointers. For those, we don't have FUNCTION_TRACER automatically
> @@ -1022,10 +1089,11 @@ include $(addprefix $(srctree)/, $(include-y))
> # Do not add $(call cc-option,...) below this line. When you build the kernel
> # from the clean source tree, the GCC plugins do not exist at this point.
>
> -# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
> +# Add user supplied CPPFLAGS, AFLAGS, CFLAGS and RUSTFLAGS as the last assignments
> KBUILD_CPPFLAGS += $(KCPPFLAGS)
> KBUILD_AFLAGS += $(KAFLAGS)
> KBUILD_CFLAGS += $(KCFLAGS)
> +KBUILD_RUSTFLAGS += $(KRUSTFLAGS)
>
> KBUILD_LDFLAGS_MODULE += --build-id=sha1
> LDFLAGS_vmlinux += --build-id=sha1
> @@ -1095,6 +1163,11 @@ ifeq ($(KBUILD_EXTMOD),)
> core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/
> core-$(CONFIG_BLOCK) += block/
>
> +# Keep this one as an `ifdef` block since its `Makefile` runs `rustc`.
> +ifdef CONFIG_RUST
> +core-y += rust/
> +endif
> +
> vmlinux-dirs := $(patsubst %/,%,$(filter %/, \
> $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
> $(libs-y) $(libs-m)))
> @@ -1195,6 +1268,9 @@ archprepare: outputmakefile archheaders archscripts scripts include/config/kerne
> prepare0: archprepare
> $(Q)$(MAKE) $(build)=scripts/mod
> $(Q)$(MAKE) $(build)=.
> +ifdef CONFIG_RUST
> + $(Q)$(MAKE) $(build)=rust
> +endif
>
> # All the preparing..
> prepare: prepare0
> @@ -1495,7 +1571,7 @@ endif # CONFIG_MODULES
> # Directories & files removed with 'make clean'
> CLEAN_FILES += include/ksym vmlinux.symvers modules-only.symvers \
> modules.builtin modules.builtin.modinfo modules.nsdeps \
> - compile_commands.json .thinlto-cache
> + compile_commands.json .thinlto-cache rust/test rust/doc
>
> # Directories & files removed with 'make mrproper'
> MRPROPER_FILES += include/config include/generated \
> @@ -1506,7 +1582,8 @@ MRPROPER_FILES += include/config include/generated \
> certs/signing_key.pem certs/signing_key.x509 \
> certs/x509.genkey \
> vmlinux-gdb.py \
> - *.spec
> + *.spec \
> + rust/*_generated.h rust/*_generated.rs rust/libmacros.so
>
> # clean - Delete most, but leave enough to build external modules
> #
> @@ -1618,6 +1695,17 @@ help:
> @echo ' kselftest-merge - Merge all the config dependencies of'
> @echo ' kselftest to existing .config.'
> @echo ''
> + @echo 'Rust targets:'
> + @echo ' rustfmt - Reformat all the Rust code in the kernel'
> + @echo ' rustfmtcheck - Checks if all the Rust code in the kernel'
> + @echo ' is formatted, printing a diff otherwise.'
> + @echo ' rustdoc - Generate Rust documentation'
> + @echo ' (requires kernel .config)'
> + @echo ' rusttest - Runs the Rust tests'
> + @echo ' (requires kernel .config; downloads external repos)'
> + @echo ' rust-analyzer - Generate rust-project.json rust-analyzer support file'
> + @echo ' (requires kernel .config)'
> + @echo ''
> @$(if $(dtstree), \
> echo 'Devicetree:'; \
> echo '* dtbs - Build device tree blobs for enabled boards'; \
> @@ -1689,6 +1777,47 @@ PHONY += $(DOC_TARGETS)
> $(DOC_TARGETS):
> $(Q)$(MAKE) $(build)=Documentation $@
>
> +
> +# Rust targets
> +# ---------------------------------------------------------------------------
> +
> +# Documentation target
> +#
> +# Using the singular to avoid running afoul of `no-dot-config-targets`.
> +PHONY += rustdoc
> +rustdoc: prepare0
> + $(Q)$(MAKE) $(build)=rust $@
> +
> +# Testing target
> +PHONY += rusttest
> +rusttest: prepare0
> + $(Q)$(MAKE) $(build)=rust $@
> +
> +# Formatting targets
> +PHONY += rustfmt rustfmtcheck
> +
> +# We skip `rust/alloc` since we want to minimize the diff w.r.t. upstream.
> +#
> +# We match using absolute paths since `find` does not resolve them
> +# when matching, which is a problem when e.g. `srctree` is `..`.
> +# We `grep` afterwards in order to remove the directory entry itself.
> +rustfmt:
> + $(Q)find $(abs_srctree) -type f -name '*.rs' \
> + -o -path $(abs_srctree)/rust/alloc -prune \
> + -o -path $(abs_objtree)/rust/test -prune \
> + | grep -Fv $(abs_srctree)/rust/alloc \
> + | grep -Fv $(abs_objtree)/rust/test \
> + | grep -Fv generated \
> + | xargs $(RUSTFMT) $(rustfmt_flags)
> +
> +rustfmtcheck: rustfmt_flags = --check
> +rustfmtcheck: rustfmt
> +
> +# IDE support targets
> +PHONY += rust-analyzer
> +rust-analyzer: prepare0
> + $(Q)$(MAKE) $(build)=rust $@
> +
> # Misc
> # ---------------------------------------------------------------------------
>
> @@ -1856,6 +1985,7 @@ clean: $(clean-dirs)
> $(call cmd,rmfiles)
> @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
> \( -name '*.[aios]' -o -name '*.ko' -o -name '.*.cmd' \
> + -o -name '*.rmeta' \
> -o -name '*.ko.*' \
> -o -name '*.dtb' -o -name '*.dtbo' -o -name '*.dtb.S' -o -name '*.dt.yaml' \
> -o -name '*.dwo' -o -name '*.lst' \
> diff --git a/arch/arm/rust/target.json b/arch/arm/rust/target.json
> new file mode 100644
> index 000000000000..3f845b8221dc
> --- /dev/null
> +++ b/arch/arm/rust/target.json
> @@ -0,0 +1,27 @@
> +{
> + "arch": "arm",
> + "crt-static-respected": true,
> + "data-layout": "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
> + "dynamic-linking": true,
> + "env": "gnu",
> + "executables": true,
> + "features": "+strict-align,+v6",
> + "function-sections": false,
> + "has-elf-tls": true,
> + "has-rpath": true,
> + "linker-is-gnu": true,
> + "llvm-target": "arm-unknown-linux-gnueabi",
> + "max-atomic-width": 64,
> + "os": "linux",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack"
> + ]
> + },
> + "relocation-model": "static",
> + "target-family": "unix",
> + "target-mcount": "\u0001__gnu_mcount_nc",
> + "target-pointer-width": "32"
> +}

oh boy. I wonder which configs change the above values? ;) Can rustc
still override these via command line flags?

> diff --git a/arch/arm64/rust/target.json b/arch/arm64/rust/target.json
> new file mode 100644
> index 000000000000..09a264df26c7
> --- /dev/null
> +++ b/arch/arm64/rust/target.json
> @@ -0,0 +1,34 @@
> +{
> + "arch": "aarch64",
> + "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
> + "disable-redzone": true,
> + "emit-debug-gdb-scripts": false,
> + "env": "gnu",
> + "features": "+strict-align,+neon,+fp-armv8",
> + "frame-pointer": "always",
> + "function-sections": false,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "aarch64-unknown-none",
> + "max-atomic-width": 128,
> + "needs-plt": true,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m64"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "stack-probes": {
> + "kind": "none"
> + },
> + "target-c-int-width": "32",
> + "target-endian": "little",
> + "target-pointer-width": "64",
> + "vendor": ""
> +}
> diff --git a/arch/powerpc/rust/target.json b/arch/powerpc/rust/target.json
> new file mode 100644
> index 000000000000..2420c8e6a520
> --- /dev/null
> +++ b/arch/powerpc/rust/target.json
> @@ -0,0 +1,29 @@
> +{
> + "arch": "powerpc64",
> + "code-model": "large",
> + "cpu": "ppc64le",
> + "data-layout": "e-m:e-i64:64-n32:64",
> + "env": "gnu",
> + "features": "-altivec,-vsx,-hard-float",
> + "function-sections": false,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "powerpc64le-elf",
> + "max-atomic-width": 64,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m64"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "target-family": "unix",
> + "target-mcount": "_mcount",
> + "target-endian": "little",
> + "target-pointer-width": "64"
> +}
> diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
> index 8a107ed18b0d..0487db1e54c6 100644
> --- a/arch/riscv/Makefile
> +++ b/arch/riscv/Makefile
> @@ -52,6 +52,7 @@ riscv-march-$(CONFIG_FPU) := $(riscv-march-y)fd
> riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c
> KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y))
> KBUILD_AFLAGS += -march=$(riscv-march-y)
> +KBUILD_RUST_TARGET := $(srctree)/arch/riscv/rust/$(subst fd,,$(riscv-march-y)).json
>
> KBUILD_CFLAGS += -mno-save-restore
> KBUILD_CFLAGS += -DCONFIG_PAGE_OFFSET=$(CONFIG_PAGE_OFFSET)
> diff --git a/arch/riscv/rust/rv32ima.json b/arch/riscv/rust/rv32ima.json
> new file mode 100644
> index 000000000000..bcdda88c1604
> --- /dev/null
> +++ b/arch/riscv/rust/rv32ima.json
> @@ -0,0 +1,36 @@
> +{
> + "arch": "riscv32",
> + "code-model": "medium",
> + "cpu": "generic-rv32",
> + "data-layout": "e-m:e-p:32:32-i64:64-n32-S128",
> + "disable-redzone": true,
> + "emit-debug-gdb-scripts": false,
> + "env": "gnu",
> + "features": "+m,+a",
> + "frame-pointer": "always",
> + "function-sections": false,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "riscv32",
> + "max-atomic-width": 32,
> + "needs-plt": true,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m32"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "stack-probes": {
> + "kind": "none"
> + },
> + "target-c-int-width": "32",
> + "target-endian": "little",
> + "target-pointer-width": "32",
> + "vendor": ""
> +}
> diff --git a/arch/riscv/rust/rv32imac.json b/arch/riscv/rust/rv32imac.json
> new file mode 100644
> index 000000000000..45873c10a5c3
> --- /dev/null
> +++ b/arch/riscv/rust/rv32imac.json
> @@ -0,0 +1,36 @@
> +{
> + "arch": "riscv32",
> + "code-model": "medium",
> + "cpu": "generic-rv32",
> + "data-layout": "e-m:e-p:32:32-i64:64-n32-S128",
> + "disable-redzone": true,
> + "emit-debug-gdb-scripts": false,
> + "env": "gnu",
> + "features": "+m,+a,+c",
> + "frame-pointer": "always",
> + "function-sections": false,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "riscv32",
> + "max-atomic-width": 32,
> + "needs-plt": true,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m32"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "stack-probes": {
> + "kind": "none"
> + },
> + "target-c-int-width": "32",
> + "target-endian": "little",
> + "target-pointer-width": "32",
> + "vendor": ""
> +}
> diff --git a/arch/riscv/rust/rv64ima.json b/arch/riscv/rust/rv64ima.json
> new file mode 100644
> index 000000000000..853d758c5461
> --- /dev/null
> +++ b/arch/riscv/rust/rv64ima.json
> @@ -0,0 +1,36 @@
> +{
> + "arch": "riscv64",
> + "code-model": "medium",
> + "cpu": "generic-rv64",
> + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n64-S128",
> + "disable-redzone": true,
> + "emit-debug-gdb-scripts": false,
> + "env": "gnu",
> + "features": "+m,+a",
> + "frame-pointer": "always",
> + "function-sections": false,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "riscv64",
> + "max-atomic-width": 64,
> + "needs-plt": true,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m64"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "stack-probes": {
> + "kind": "none"
> + },
> + "target-c-int-width": "32",
> + "target-endian": "little",
> + "target-pointer-width": "64",
> + "vendor": ""
> +}
> diff --git a/arch/riscv/rust/rv64imac.json b/arch/riscv/rust/rv64imac.json
> new file mode 100644
> index 000000000000..ce50ee8e8c93
> --- /dev/null
> +++ b/arch/riscv/rust/rv64imac.json
> @@ -0,0 +1,36 @@
> +{
> + "arch": "riscv64",
> + "code-model": "medium",
> + "cpu": "generic-rv64",
> + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n64-S128",
> + "disable-redzone": true,
> + "emit-debug-gdb-scripts": false,
> + "env": "gnu",
> + "features": "+m,+a,+c",
> + "frame-pointer": "always",
> + "function-sections": false,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "riscv64",
> + "max-atomic-width": 64,
> + "needs-plt": true,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m64"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "stack-probes": {
> + "kind": "none"
> + },
> + "target-c-int-width": "32",
> + "target-endian": "little",
> + "target-pointer-width": "64",
> + "vendor": ""
> +}
> diff --git a/arch/x86/rust/target.json b/arch/x86/rust/target.json
> new file mode 100644
> index 000000000000..379cf39e8941
> --- /dev/null
> +++ b/arch/x86/rust/target.json
> @@ -0,0 +1,36 @@
> +{
> + "arch": "x86_64",
> + "code-model": "kernel",
> + "cpu": "x86-64",
> + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
> + "disable-redzone": true,
> + "emit-debug-gdb-scripts": false,
> + "env": "gnu",
> + "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float",
> + "frame-pointer": "always",
> + "function-sections": false,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "x86_64-elf",
> + "max-atomic-width": 64,
> + "needs-plt": true,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m64"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "stack-probes": {
> + "kind": "none"
> + },
> + "target-c-int-width": "32",
> + "target-endian": "little",
> + "target-pointer-width": "64",
> + "vendor": "unknown"
> +}
> diff --git a/init/Kconfig b/init/Kconfig
> index 4b7bac10c72d..03d3c21e28a3 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -60,6 +60,15 @@ config LLD_VERSION
> default $(ld-version) if LD_IS_LLD
> default 0
>
> +config HAS_RUST
> + depends on ARM64 || CPU_32v6 || CPU_32v6K || (PPC64 && CPU_LITTLE_ENDIAN) || X86_64 || RISCV
> + def_bool $(success,$(RUSTC) --version)
> +
> +config RUSTC_VERSION
> + depends on HAS_RUST
> + int
> + default $(shell,$(srctree)/scripts/rust-version.sh $(RUSTC))
> +
> config CC_CAN_LINK
> bool
> default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(m64-flag)) if 64BIT
> @@ -142,7 +151,8 @@ config WERROR
> default COMPILE_TEST
> help
> A kernel build should not cause any compiler warnings, and this
> - enables the '-Werror' flag to enforce that rule by default.
> + enables the '-Werror' (for C) and '-Dwarnings' (for Rust) flags
> + to enforce that rule by default.
>
> However, if you have a new (or very old) compiler with odd and
> unusual warnings, or you have some architecture with problems,
> @@ -2044,6 +2054,25 @@ config PROFILING
> Say Y here to enable the extended profiling support mechanisms used
> by profilers.
>
> +config RUST
> + bool "Rust support"
> + depends on HAS_RUST
> + depends on !COMPILE_TEST
> + depends on !MODVERSIONS
> + default n
> + help
> + Enables Rust support in the kernel.
> +
> + This allows other Rust-related options, like drivers written in Rust,
> + to be selected.
> +
> + It is also required to be able to load external kernel modules
> + written in Rust.
> +
> + See Documentation/rust/ for more information.
> +
> + If unsure, say N.
> +
> #
> # Place an empty function call at each tracepoint site. Can be
> # dynamically changed for a probe function.
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 5c12bde10996..361379695f18 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -2650,6 +2650,150 @@ config HYPERV_TESTING
>
> endmenu # "Kernel Testing and Coverage"
>
> +menu "Rust hacking"
> +
> +config RUST_DEBUG_ASSERTIONS
> + bool "Debug assertions"
> + default n
> + depends on RUST
> + help
> + Enables rustc's `-Cdebug-assertions` codegen option.
> +
> + This flag lets you turn `cfg(debug_assertions)` conditional
> + compilation on or off. This can be used to enable extra debugging
> + code in development but not in production. For example, it controls
> + the behavior of the standard library's `debug_assert!` macro.
> +
> + Note that this will apply to all Rust code, including `core`.
> +
> + If unsure, say N.
> +
> +config RUST_OVERFLOW_CHECKS
> + bool "Overflow checks"
> + default y

So this is like turning on -fsanitize=signed-integer-overflow and
-fsanitize=unsigned-integer-overflow by default?

> + depends on RUST
> + help
> + Enables rustc's `-Coverflow-checks` codegen option.
> +
> + This flag allows you to control the behavior of runtime integer
> + overflow. When overflow-checks are enabled, a panic will occur
> + on overflow.

A rust panic or a kernel panic?

> +
> + Note that this will apply to all Rust code, including `core`.
> +
> + If unsure, say Y.
> +
> +choice
> + prompt "Optimization level"
> + default RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C
> + depends on RUST
> + help
> + Controls rustc's `-Copt-level` codegen option.
> +
> + This flag controls the optimization level.
> +
> + If unsure, say "Similar as chosen for C".
> +
> +config RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C
> + bool "Similar as chosen for C"
> + help
> + This choice will pick a similar optimization level as chosen in
> + the "Compiler optimization level" for C:
> +
> + -O2 is currently mapped to -Copt-level=2
> + -O3 is currently mapped to -Copt-level=3
> + -Os is currently mapped to -Copt-level=z
> +
> + The mapping may change over time to follow the intended semantics
> + of the choice for C as sensibly as possible.
> +
> + This is the default.

This should really be the only option here. I still don't see the
point of allowing wacky combinations of configs where we differ from C
code. The more combinations will just frustrate randconfig builds.

> +
> +config RUST_OPT_LEVEL_0
> + bool "No optimizations (-Copt-level=0)"
> + help
> + Not recommended for most purposes. It may come in handy for debugging
> + suspected optimizer bugs, unexpected undefined behavior, etc.
> +
> + Note that this level will *not* enable debug assertions nor overflow
> + checks on its own (like it happens when interacting with rustc
> + directly). Use the corresponding configuration options to control
> + that instead, orthogonally.
> +
> + Note this level may cause excessive stack usage, which can lead to stack
> + overflow and subsequent crashes.
> +
> +config RUST_OPT_LEVEL_1
> + bool "Basic optimizations (-Copt-level=1)"
> + help
> + Useful for debugging without getting too lost, but without
> + the overhead and boilerplate of no optimizations at all.
> +
> + Note this level may cause excessive stack usage, which can lead to stack
> + overflow and subsequent crashes.
> +
> +config RUST_OPT_LEVEL_2
> + bool "Some optimizations (-Copt-level=2)"
> + help
> + The sensible choice in most cases.
> +
> +config RUST_OPT_LEVEL_3
> + bool "All optimizations (-Copt-level=3)"
> + help
> + Yet more performance (hopefully).
> +
> +config RUST_OPT_LEVEL_S
> + bool "Optimize for size (-Copt-level=s)"
> + help
> + Smaller kernel, ideally without too much performance loss.
> +
> +config RUST_OPT_LEVEL_Z
> + bool "Optimize for size, no loop vectorization (-Copt-level=z)"
> + help
> + Like the previous level, but also turn off loop vectorization.
> +
> +endchoice
> +
> +choice
> + prompt "Build-time assertions"
> + default RUST_BUILD_ASSERT_ALLOW if RUST_OPT_LEVEL_0
> + default RUST_BUILD_ASSERT_DENY if !RUST_OPT_LEVEL_0
> + 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.
> + You can choose to abort compilation or ignore them during build and let the
> + check be carried to runtime.
> +
> + If optimizations are turned off, you cannot select "Deny".
> +
> + If unsure, say "Deny".
> +
> +config RUST_BUILD_ASSERT_ALLOW
> + bool "Allow"
> + help
> + Unoptimized calls to `build_error!` will be converted to `panic!`
> + and checked at runtime.
> +
> +config RUST_BUILD_ASSERT_WARN
> + bool "Warn"
> + help
> + Unoptimized calls to `build_error!` will be converted to `panic!`
> + and checked at runtime, but warnings will be generated when building.
> +
> +config RUST_BUILD_ASSERT_DENY
> + bool "Deny"
> + depends on !RUST_OPT_LEVEL_0
> + help
> + Unoptimized calls to `build_error!` will abort compilation.
> +
> +endchoice
> +
> +
> +endmenu # "Rust"
> +
> source "Documentation/Kconfig"
>
> endmenu # Kernel hacking
> diff --git a/rust/.gitignore b/rust/.gitignore
> new file mode 100644
> index 000000000000..168cb26a31b9
> --- /dev/null
> +++ b/rust/.gitignore
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +bindings_generated.rs
> +bindings_helpers_generated.rs
> +exports_*_generated.h
> +doc/
> +test/
> diff --git a/rust/Makefile b/rust/Makefile
> new file mode 100644
> index 000000000000..2d08314e7031
> --- /dev/null
> +++ b/rust/Makefile
> @@ -0,0 +1,353 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_RUST) += core.o compiler_builtins.o helpers.o
> +extra-$(CONFIG_RUST) += exports_core_generated.h
> +
> +extra-$(CONFIG_RUST) += libmacros.so
> +
> +extra-$(CONFIG_RUST) += bindings_generated.rs bindings_helpers_generated.rs
> +obj-$(CONFIG_RUST) += alloc.o kernel.o
> +extra-$(CONFIG_RUST) += exports_alloc_generated.h exports_kernel_generated.h
> +
> +ifdef CONFIG_RUST_BUILD_ASSERT_DENY
> +extra-$(CONFIG_RUST) += build_error.o
> +else
> +obj-$(CONFIG_RUST) += build_error.o
> +endif
> +
> +obj-$(CONFIG_RUST) += exports.o
> +
> +ifeq ($(quiet),silent_)
> +cargo_quiet=-q
> +rust_test_quiet=-q
> +rustdoc_test_quiet=--test-args -q
> +else ifeq ($(quiet),quiet_)
> +rust_test_quiet=-q
> +rustdoc_test_quiet=--test-args -q
> +else
> +cargo_quiet=--verbose
> +endif
> +
> +core-cfgs = \
> + --cfg no_fp_fmt_parse
> +
> +alloc-cfgs = \
> + --cfg no_global_oom_handling \
> + --cfg no_rc \
> + --cfg no_sync
> +
> +quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
> + cmd_rustdoc = \
> + OBJTREE=$(abspath $(objtree)) \
> + $(RUSTDOC) $(if $(rustdoc_host),,$(rust_cross_flags)) \
> + $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rust_flags))) \
> + $(rustc_target_flags) -L $(objtree)/rust \
> + --output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \
> + @$(objtree)/include/generated/rustc_cfg $<
> +
> +rustdoc: rustdoc-core rustdoc-macros rustdoc-compiler_builtins rustdoc-alloc rustdoc-kernel
> + $(Q)cp $(srctree)/Documentation/rust/assets/* $(objtree)/rust/doc
> +
> +rustdoc-macros: private rustdoc_host = yes
> +rustdoc-macros: private rustc_target_flags = --crate-type proc-macro \
> + --extern proc_macro
> +rustdoc-macros: $(srctree)/rust/macros/lib.rs FORCE
> + $(call if_changed,rustdoc)
> +
> +rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs rustdoc-core FORCE
> + $(call if_changed,rustdoc)
> +
> +# We need to allow `rustdoc::broken_intra_doc_links` because some
> +# `no_global_oom_handling` functions refer to non-`no_global_oom_handling`
> +# functions. Ideally `rustdoc` would have a way to distinguish broken links
> +# due to things that are "configured out" vs. entirely non-existing ones.
> +rustdoc-alloc: private rustc_target_flags = $(alloc-cfgs) \
> + -Arustdoc::broken_intra_doc_links
> +rustdoc-alloc: $(srctree)/rust/alloc/lib.rs rustdoc-core \
> + rustdoc-compiler_builtins FORCE
> + $(call if_changed,rustdoc)
> +
> +rustdoc-kernel: private rustc_target_flags = --extern alloc \
> + --extern build_error \
> + --extern macros=$(objtree)/rust/libmacros.so
> +rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-core \
> + rustdoc-macros rustdoc-compiler_builtins rustdoc-alloc \
> + $(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs \
> + $(objtree)/rust/bindings_helpers_generated.rs FORCE
> + $(call if_changed,rustdoc)
> +
> +quiet_cmd_rustc_test_library = RUSTC TL $<
> + cmd_rustc_test_library = \
> + OBJTREE=$(abspath $(objtree)) \
> + $(RUSTC) $(filter-out --sysroot=%, $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rust_flags)))) \
> + $(rustc_target_flags) --crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
> + --out-dir $(objtree)/rust/test/ --cfg testlib \
> + --sysroot $(objtree)/rust/test/sysroot \
> + -L $(objtree)/rust/test/ --crate-name $(subst rusttest-,,$(subst rusttestlib-,,$@)) $<
> +
> +rusttestlib-build_error: $(srctree)/rust/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: $(srctree)/rust/macros/lib.rs rusttest-prepare FORCE
> + $(call if_changed,rustc_test_library)
> +
> +quiet_cmd_rustdoc_test = RUSTDOC T $<
> + cmd_rustdoc_test = \
> + OBJTREE=$(abspath $(objtree)) \
> + $(RUSTDOC) --test $(filter-out --sysroot=%, $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rust_flags)))) \

Seeing repeated filter-out rules makes me wonder if we could DRY up
some of these rules with function definitions?

> + $(rustc_target_flags) $(rustdoc_test_target_flags) \
> + --sysroot $(objtree)/rust/test/sysroot $(rustdoc_test_quiet) \
> + -L $(objtree)/rust/test \
> + --output $(objtree)/rust/doc --crate-name $(subst rusttest-,,$@) \
> + @$(objtree)/include/generated/rustc_cfg $<
> +
> +# We cannot use `-Zpanic-abort-tests` because some tests are dynamic,
> +# so for the moment we skip `-Cpanic=abort`.
> +quiet_cmd_rustc_test = RUSTC T $<
> + cmd_rustc_test = \
> + OBJTREE=$(abspath $(objtree)) \
> + $(RUSTC) --test $(filter-out --sysroot=%, $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rust_flags)))) \
> + $(rustc_target_flags) --out-dir $(objtree)/rust/test \
> + --sysroot $(objtree)/rust/test/sysroot \
> + -L $(objtree)/rust/test/ --crate-name $(subst rusttest-,,$@) $<; \
> + $(objtree)/rust/test/$(subst rusttest-,,$@) $(rust_test_quiet) \
> + $(rustc_test_run_flags)
> +
> +rusttest: rusttest-macros rusttest-kernel
> +
> +# This prepares a custom sysroot with our custom `alloc` instead of
> +# the standard one.
> +#
> +# This requires several hacks:
> +# - Unlike `core` and `alloc`, `std` depends on more than a dozen crates,
> +# including third-party crates that need to be downloaded, plus custom
> +# `build.rs` steps. Thus hardcoding things here is not maintainable.
> +# - `cargo` knows how to build the standard library, but it is an unstable
> +# feature so far (`-Zbuild-std`).
> +# - `cargo` only considers the use case of building the standard library
> +# to use it in a given package. Thus we need to create a dummy package
> +# and pick the generated libraries from there.
> +# - Since we only keep a subset of upstream `alloc` in-tree, we need
> +# to recreate it on the fly by putting our sources on top.
> +# - The usual ways of modifying the dependency graph in `cargo` do not seem
> +# to apply for the `-Zbuild-std` steps, thus we have to mislead it
> +# by modifying the sources in the sysroot.
> +# - To avoid messing with the user's Rust installation, we create a clone
> +# of the sysroot. However, `cargo` ignores `RUSTFLAGS` in the `-Zbuild-std`
> +# steps, thus we use a wrapper binary passed via `RUSTC` to pass the flag.

Are there links to corresponding feature requests upstream so that one
day we can drop technical debt here? In particular, having to create
a custom sysroot as is done here isn't particularly pretty.

I'd be curious if we could remove the dependency on cargo for rust tests.

> +#
> +# In the future, we hope to avoid the whole ordeal by either:
> +# - Making the `test` crate not depend on `std` (either improving upstream
> +# or having our own custom crate).
> +# - Making the tests run in kernel space (requires the previous point).
> +# - Making `std` and friends be more like a "normal" crate, so that
> +# `-Zbuild-std` and related hacks are not needed.
> +quiet_cmd_rustsysroot = RUSTSYSROOT
> + cmd_rustsysroot = \
> + rm -rf $(objtree)/rust/test; \
> + mkdir -p $(objtree)/rust/test; \
> + cp -a $(rustc_sysroot) $(objtree)/rust/test/sysroot; \
> + cp -r $(srctree)/rust/alloc/* \
> + $(objtree)/rust/test/sysroot/lib/rustlib/src/rust/library/alloc/src; \
> + echo '\#!/bin/sh' > $(objtree)/rust/test/rustc_sysroot; \
> + echo "$(RUSTC) --sysroot=$(abspath $(objtree)/rust/test/sysroot) \"\$$@\"" \
> + >> $(objtree)/rust/test/rustc_sysroot; \
> + chmod u+x $(objtree)/rust/test/rustc_sysroot; \
> + $(CARGO) -q new $(objtree)/rust/test/dummy; \
> + RUSTC=$(objtree)/rust/test/rustc_sysroot $(CARGO) $(cargo_quiet) \
> + test -Zbuild-std --target $(rustc_host_target) \
> + --manifest-path $(objtree)/rust/test/dummy/Cargo.toml; \
> + rm $(objtree)/rust/test/sysroot/lib/rustlib/$(rustc_host_target)/lib/*; \
> + cp $(objtree)/rust/test/dummy/target/$(rustc_host_target)/debug/deps/* \
> + $(objtree)/rust/test/sysroot/lib/rustlib/$(rustc_host_target)/lib
> +
> +rusttest-prepare: FORCE
> + $(call if_changed,rustsysroot)
> +
> +rusttest-macros: private rustc_target_flags = --extern proc_macro
> +rusttest-macros: private rustdoc_test_target_flags = --crate-type proc-macro
> +rusttest-macros: $(srctree)/rust/macros/lib.rs rusttest-prepare FORCE
> + $(call if_changed,rustc_test)
> + $(call if_changed,rustdoc_test)
> +
> +rusttest-kernel: private rustc_target_flags = --extern alloc \
> + --extern build_error --extern macros
> +rusttest-kernel: private rustc_test_run_flags = \
> + --skip bindgen_test_layout_
> +rusttest-kernel: $(srctree)/rust/kernel/lib.rs rusttest-prepare \
> + rusttestlib-build_error rusttestlib-macros FORCE
> + $(call if_changed,rustc_test)
> + $(call if_changed,rustc_test_library)
> + $(call if_changed,rustdoc_test)
> +
> +ifdef CONFIG_CC_IS_CLANG
> +bindgen_c_flags = $(c_flags)
> +else
> +# bindgen relies on libclang to parse C. Ideally, bindgen would support a GCC
> +# plugin backend and/or the Clang driver would be perfectly compatible with GCC.
> +#
> +# For the moment, here we are tweaking the flags on the fly. Some config
> +# options may not work (e.g. `GCC_PLUGIN_RANDSTRUCT` if we end up using one

Do we denote the conflict in KCONFIG?

> +# of those structs).
> +bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \
> + -mskip-rax-setup -mgeneral-regs-only -msign-return-address=% \
> + -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount \
> + -mabi=lp64 -mstack-protector-guard% -fconserve-stack -falign-jumps=% \
> + -falign-loops=% -femit-struct-debug-baseonly \
> + -fno-ipa-cp-clone -fno-ipa-sra -fno-partial-inlining \
> + -fplugin-arg-arm_ssp_per_task_plugin-% \
> + -fno-reorder-blocks -fno-allow-store-data-races -fasan-shadow-offset=% \
> + -Wno-packed-not-aligned -Wno-format-truncation -Wno-format-overflow \
> + -Wno-stringop-truncation -Wno-unused-but-set-variable \
> + -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized \
> + -Werror=designated-init -Wno-zero-length-bounds \
> + -Wno-alloc-size-larger-than --param=% --param asan-%

Why do we need to strip out all these warning flags when running
bindgen? Should be just be using -w to silence all warnings from
clang when invoking bindgen?

> +
> +# We need to keep the quotes for this one -- it comes from a `Kconfig`
> +bindgen_skip_c_flags += "-Wimplicit-fallthrough=%"
> +
> +# PowerPC
> +bindgen_skip_c_flags += -mtraceback=no -mno-pointers-to-nested-functions \
> + -mno-string -mno-strict-align

I don't understand why the PowerPC specific flags are pulled out, but
the x86 specific ones (like most of the -m flags in the initial
defiition of bindgen_skip_c_flags) are not.

> +
> +# Derived from `scripts/Makefile.clang`
> +BINDGEN_TARGET_arm := arm-linux-gnueabi
> +BINDGEN_TARGET_arm64 := aarch64-linux-gnu
> +BINDGEN_TARGET_powerpc := powerpc64le-linux-gnu
> +BINDGEN_TARGET_riscv := riscv64-linux-gnu
> +BINDGEN_TARGET_x86 := x86_64-linux-gnu
> +BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH))
> +
> +bindgen_extra_c_flags = --target=$(BINDGEN_TARGET) \
> + -Wno-address-of-packed-member \
> + -Wno-gnu-variable-sized-type-not-at-end
> +bindgen_c_flags = $(filter-out $(bindgen_skip_c_flags), $(c_flags)) \
> + $(bindgen_extra_c_flags)
> +endif
> +
> +ifdef CONFIG_LTO
> +bindgen_c_flags_lto = $(filter-out $(CC_FLAGS_LTO), $(bindgen_c_flags))
> +else
> +bindgen_c_flags_lto = $(bindgen_c_flags)
> +endif
> +
> +# To avoid several recompilations in PowerPC, which inserts `-D_TASK_CPU`
> +bindgen_c_flags_final = $(filter-out -D_TASK_CPU=%, $(bindgen_c_flags_lto))
> +
> +quiet_cmd_bindgen = BINDGEN $@
> + cmd_bindgen = \
> + $(BINDGEN) $< $(shell grep -v '^\#\|^$$' $(srctree)/rust/bindgen_parameters) \
> + --use-core --with-derive-default --ctypes-prefix c_types \
> + --no-debug '.*' \
> + --size_t-is-usize -o $@ -- $(bindgen_c_flags_final) -DMODULE
> +
> +$(objtree)/rust/bindings_generated.rs: $(srctree)/rust/kernel/bindings_helper.h \
> + $(srctree)/rust/bindgen_parameters FORCE
> + $(call if_changed_dep,bindgen)
> +
> +quiet_cmd_bindgen_helper = BINDGEN $@
> + cmd_bindgen_helper = \
> + $(BINDGEN) $< --blacklist-type '.*' --whitelist-var '' \
> + --whitelist-function 'rust_helper_.*' \
> + --use-core --with-derive-default --ctypes-prefix c_types \
> + --no-debug '.*' \
> + --size_t-is-usize -o $@ -- $(bindgen_c_flags_final) \
> + -I$(objtree)/rust/ -DMODULE; \
> + sed -Ei 's/pub fn rust_helper_([a-zA-Z0-9_]*)/\#[link_name="rust_helper_\1"]\n pub fn \1/g' $@
> +
> +$(objtree)/rust/bindings_helpers_generated.rs: $(srctree)/rust/helpers.c FORCE
> + $(call if_changed_dep,bindgen_helper)
> +
> +quiet_cmd_exports = EXPORTS $@
> + cmd_exports = \
> + $(NM) -p --defined-only $< \
> + | grep -E ' (T|R|D) ' | cut -d ' ' -f 3 \
> + | xargs -Isymbol \
> + echo 'EXPORT_SYMBOL_RUST_GPL(symbol);' > $@

This doesn't accidentally export non-GPL symbols as GPL does it?

... snip ...

--
Thanks,
~Nick Desaulniers