Skip to contentSkip to conversation

FAILED: patch "[PATCH] rust: use `#[used(compiler)]` to fix build and `modpost` with" failed to apply to 6.12-stable tree

PATCH In review [email protected] 3 participants 3 messages Jul 21, 2025 – Jul 23, 2025

Subsystems (6)
lore
Share & tools
lore

[email protected]

author maintainer
#0 lore raw

The patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 7498159226772d66f150dd406be462d75964a366
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025072102-theorize-single-3700@gregkh' --subject-prefix 'PATCH 6.12.y' HEAD^..

Possible dependencies:

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From 7498159226772d66f150dd406be462d75964a366 Mon Sep 17 00:00:00 2001
From: Miguel Ojeda <[email protected]>
Date: Sat, 12 Jul 2025 18:01:03 +0200
Subject: [PATCH] rust: use #[used(compiler)] to fix build and modpost with
Rust >= 1.89.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails to build the rusttest target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like modpost gets confused:

WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1] treat #[used] as #[used(linker)] instead of #[used(compiler)] for our targets. This means that the retain section flag (R, SHF_GNU_RETAIN) will be used and that they will be marked as unique too, with different IDs. In turn, that means we end up with undefined references that did not get discarded in rusttest and that multiple .modinfo sections are generated, which confuse tooling like modpost because they only expect one.

Thus start using #[used(compiler)] to keep the previous behavior and to be explicit about what we want. Sadly, it is an unstable feature (used_with_arg) [2] -- we will talk to upstream Rust about it. The good news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they behave the same way as before [4].

Alternatively, we could use #[no_mangle] or #[export_name = ...] since those still behave like #[used(compiler)], but of course it is not really what we want to express, and it requires other changes to avoid symbol conflicts.

rust/Makefile +1 -0
index 27dec7904c3a..115b63b7d1e3 100644
@@ -194,6 +194,7 @@ quiet_cmd_rustdoc_test = RUSTDOC T $<
 	RUST_MODFILE=test.rs \
 	OBJTREE=$(abspath $(objtree)) \
 	$(RUSTDOC) --test $(rust_common_flags) \
+		-Zcrate-attr='feature(used_with_arg)' \
 		@$(objtree)/include/generated/rustc_cfg \
 		$(rustc_target_flags) $(rustdoc_test_target_flags) \
 		$(rustdoc_test_quiet) \
rust/kernel/firmware.rs +1 -1
index 2494c96e105f..4fe621f35716 100644
@@ -202,7 +202,7 @@ macro_rules! module_firmware {
             };
 
             #[link_section = ".modinfo"]
-            #[used]
+            #[used(compiler)]
             static __MODULE_FIRMWARE: [u8; $($builder)*::create(__MODULE_FIRMWARE_PREFIX)
                 .build_length()] = $($builder)*::create(__MODULE_FIRMWARE_PREFIX).build();
         };
rust/kernel/kunit.rs +1 -1
index 4b8cdcb21e77..b9e65905e121 100644
@@ -302,7 +302,7 @@ macro_rules! kunit_unsafe_test_suite {
                     is_init: false,
                 };
 
-            #[used]
+            #[used(compiler)]
             #[allow(unused_unsafe)]
             #[cfg_attr(not(target_os = "macos"), link_section = ".kunit_test_suites")]
             static mut KUNIT_TEST_SUITE_ENTRY: *const ::kernel::bindings::kunit_suite =
rust/kernel/lib.rs +3 -0
index 6b4774b2b1c3..e13d6ed88fa6 100644
@@ -34,6 +34,9 @@
 // Expected to become stable.
 #![feature(arbitrary_self_types)]
 //
+// To be determined.
+#![feature(used_with_arg)]
+//
 // `feature(derive_coerce_pointee)` is expected to become stable. Before Rust
 // 1.84.0, it did not exist, so enable the predecessor features.
 #![cfg_attr(CONFIG_RUSTC_HAS_COERCE_POINTEE, feature(derive_coerce_pointee))]
rust/macros/module.rs +5 -5
index 2ddd2eeb2852..75efc6eeeafc 100644
@@ -57,7 +57,7 @@ fn emit_base(&mut self, field: &str, content: &str, builtin: bool) {
                 {cfg}
                 #[doc(hidden)]
                 #[cfg_attr(not(target_os = \"macos\"), link_section = \".modinfo\")]
-                #[used]
+                #[used(compiler)]
                 pub static __{module}_{counter}: [u8; {length}] = *{string};
             ",
             cfg = if builtin {
@@ -249,7 +249,7 @@ mod __module_init {{
                     // key or a new section. For the moment, keep it simple.
                     #[cfg(MODULE)]
                     #[doc(hidden)]
-                    #[used]
+                    #[used(compiler)]
                     static __IS_RUST_MODULE: () = ();
 
                     static mut __MOD: ::core::mem::MaybeUninit<{type_}> =
@@ -273,7 +273,7 @@ mod __module_init {{
 
                     #[cfg(MODULE)]
                     #[doc(hidden)]
-                    #[used]
+                    #[used(compiler)]
                     #[link_section = \".init.data\"]
                     static __UNIQUE_ID___addressable_init_module: unsafe extern \"C\" fn() -> i32 = init_module;
 
@@ -293,7 +293,7 @@ mod __module_init {{
 
                     #[cfg(MODULE)]
                     #[doc(hidden)]
-                    #[used]
+                    #[used(compiler)]
                     #[link_section = \".exit.data\"]
                     static __UNIQUE_ID___addressable_cleanup_module: extern \"C\" fn() = cleanup_module;
 
@@ -303,7 +303,7 @@ mod __module_init {{
                     #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
                     #[doc(hidden)]
                     #[link_section = \"{initcall_section}\"]
-                    #[used]
+                    #[used(compiler)]
                     pub static __{ident}_initcall: extern \"C\" fn() ->
                         ::kernel::ffi::c_int = __{ident}_init;
 
scripts/Makefile.build +2 -1
index a6461ea411f7..ba71b27aa363 100644
@@ -312,10 +312,11 @@ $(obj)/%.lst: $(obj)/%.c FORCE
 #   - Stable since Rust 1.82.0: `feature(asm_const)`, `feature(raw_ref_op)`.
 #   - Stable since Rust 1.87.0: `feature(asm_goto)`.
 #   - Expected to become stable: `feature(arbitrary_self_types)`.
+#   - To be determined: `feature(used_with_arg)`.
 #
 # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
 # the unstable features in use.
-rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op
+rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op,used_with_arg
 
 # `--out-dir` is required to avoid temporaries being created by `rustc` in the
 # current working directory, which may be not accessible in the out-of-tree
maintainer
#1 lore raw

PATCH 1/1 rust: use `#[used(compiler)]` to fix build and `modpost` with Rust >= 1.89.0

Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails to build the rusttest target due to undefined references such as:

    kernel...-cgu.0:(.text....+0x116): undefined reference to
    `rust_helper_kunit_get_current_test'

Moreover, tooling like modpost gets confused:

WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o
ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o

The reason behind both issues is that the Rust compiler will now [1] treat #[used] as #[used(linker)] instead of #[used(compiler)] for our targets. This means that the retain section flag (R, SHF_GNU_RETAIN) will be used and that they will be marked as unique too, with different IDs. In turn, that means we end up with undefined references that did not get discarded in rusttest and that multiple .modinfo sections are generated, which confuse tooling like modpost because they only expect one.

Thus start using #[used(compiler)] to keep the previous behavior and to be explicit about what we want. Sadly, it is an unstable feature (used_with_arg) [2] -- we will talk to upstream Rust about it. The good news is that it has been available for a long time (Rust >= 1.60) [3].

The changes should also be fine for previous Rust versions, since they behave the same way as before [4].

Alternatively, we could use #[no_mangle] or #[export_name = ...] since those still behave like #[used(compiler)], but of course it is not really what we want to express, and it requires other changes to avoid symbol conflicts.

---
 rust/Makefile          |  1 +
 rust/kernel/lib.rs     |  1 +
 rust/macros/module.rs  | 10 +++++-----
 scripts/Makefile.build |  2 +-
 4 files changed, 8 insertions(+), 6 deletions(-)
rust/Makefile +1 -0
index b8b7f817c48e..17491d8229a4 100644
@@ -157,6 +157,7 @@ quiet_cmd_rustdoc_test = RUSTDOC T $<
       cmd_rustdoc_test = \
 	OBJTREE=$(abspath $(objtree)) \
 	$(RUSTDOC) --test $(rust_common_flags) \
+		-Zcrate-attr='feature(used_with_arg)' \
 		@$(objtree)/include/generated/rustc_cfg \
 		$(rustc_target_flags) $(rustdoc_test_target_flags) \
 		$(rustdoc_test_quiet) \
rust/kernel/lib.rs +1 -0
index 904d241604db..889ddcb1a2dd 100644
@@ -18,6 +18,7 @@
 #![feature(inline_const)]
 #![feature(lint_reasons)]
 #![feature(unsize)]
+#![feature(used_with_arg)]
 
 // Ensure conditional compilation based on the kernel configuration works;
 // otherwise we may silently break things like initcall handling.
rust/macros/module.rs +5 -5
index a5ea5850e307..edb23b28f446 100644
@@ -57,7 +57,7 @@ fn emit_base(&mut self, field: &str, content: &str, builtin: bool) {
                 {cfg}
                 #[doc(hidden)]
                 #[link_section = \".modinfo\"]
-                #[used]
+                #[used(compiler)]
                 pub static __{module}_{counter}: [u8; {length}] = *{string};
             ",
             cfg = if builtin {
@@ -230,7 +230,7 @@ mod __module_init {{
                     // key or a new section. For the moment, keep it simple.
                     #[cfg(MODULE)]
                     #[doc(hidden)]
-                    #[used]
+                    #[used(compiler)]
                     static __IS_RUST_MODULE: () = ();
 
                     static mut __MOD: Option<{type_}> = None;
@@ -253,7 +253,7 @@ mod __module_init {{
 
                     #[cfg(MODULE)]
                     #[doc(hidden)]
-                    #[used]
+                    #[used(compiler)]
                     #[link_section = \".init.data\"]
                     static __UNIQUE_ID___addressable_init_module: unsafe extern \"C\" fn() -> i32 = init_module;
 
@@ -273,7 +273,7 @@ mod __module_init {{
 
                     #[cfg(MODULE)]
                     #[doc(hidden)]
-                    #[used]
+                    #[used(compiler)]
                     #[link_section = \".exit.data\"]
                     static __UNIQUE_ID___addressable_cleanup_module: extern \"C\" fn() = cleanup_module;
 
@@ -283,7 +283,7 @@ mod __module_init {{
                     #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
                     #[doc(hidden)]
                     #[link_section = \"{initcall_section}\"]
-                    #[used]
+                    #[used(compiler)]
                     pub static __{name}_initcall: extern \"C\" fn() -> kernel::ffi::c_int = __{name}_init;
 
                     #[cfg(not(MODULE))]
scripts/Makefile.build +1 -1
index 2bba59e790b8..2c5c1a214f3b 100644
@@ -248,7 +248,7 @@ $(obj)/%.lst: $(obj)/%.c FORCE
 # Compile Rust sources (.rs)
 # ---------------------------------------------------------------------------
 
-rust_allowed_features := arbitrary_self_types,lint_reasons
+rust_allowed_features := arbitrary_self_types,lint_reasons,used_with_arg
 
 # `--out-dir` is required to avoid temporaries being created by `rustc` in the
 # current working directory, which may be not accessible in the out-of-tree
Signature · 1 line
2.50.1
maintainer Acked-by Björn Roy Baron on v1 1/1
#2 lore raw

Re: [PATCH 6.12.y] rust: use `#[used(compiler)]` to fix build and `modpost` with Rust >= 1.89.0

[ Sasha's backport helper bot ]

Hi,

✅ All tests passed successfully. No issues detected. No action required from the submitter.

The upstream commit SHA1 provided is correct: 7498159226772d66f150dd406be462d75964a366

Status in newer kernel trees:
6.15.y | Not found

Note: The patch differs from the upstream commit:
---
1: 749815922677 ! 1: 9212c12e784b rust: use #[used(compiler)] to fix build and modpost with Rust >= 1.89.0
@@ Commit message

+ (cherry picked from commit 7498159226772d66f150dd406be462d75964a366)
+ Signed-off-by: Miguel Ojeda <[email protected]>

## rust/Makefile ##
@@ rust/Makefile: quiet_cmd_rustdoc_test = RUSTDOC T $<
- RUST_MODFILE=test.rs \
+ cmd_rustdoc_test = \
OBJTREE=$(abspath $(objtree)) \
$(RUSTDOC) --test $(rust_common_flags) \
+ -Zcrate-attr='feature(used_with_arg)' \
@@ rust/Makefile: quiet_cmd_rustdoc_test = RUSTDOC T $<
$(rustc_target_flags) $(rustdoc_test_target_flags) \
$(rustdoc_test_quiet) \

- ## rust/kernel/firmware.rs ##
-@@ rust/kernel/firmware.rs: macro_rules! module_firmware {
- };
-
- #[link_section = ".modinfo"]
-- #[used]
-+ #[used(compiler)]
- static __MODULE_FIRMWARE: [u8; $($builder)*::create(__MODULE_FIRMWARE_PREFIX)
- .build_length()] = $($builder)*::create(__MODULE_FIRMWARE_PREFIX).build();
- };
-
- ## rust/kernel/kunit.rs ##
-@@ rust/kernel/kunit.rs: macro_rules! kunit_unsafe_test_suite {
- is_init: false,
- };
-
-- #[used]
-+ #[used(compiler)]
- #[allow(unused_unsafe)]
- #[cfg_attr(not(target_os = "macos"), link_section = ".kunit_test_suites")]
- static mut KUNIT_TEST_SUITE_ENTRY: *const ::kernel::bindings::kunit_suite =
-
## rust/kernel/lib.rs ##
@@
- // Expected to become stable.
- #![feature(arbitrary_self_types)]
- //
-+// To be determined.
+ #![feature(inline_const)]
+ #![feature(lint_reasons)]
+ #![feature(unsize)]
+#![feature(used_with_arg)]
-+//
- // feature(derive_coerce_pointee) is expected to become stable. Before Rust
- // 1.84.0, it did not exist, so enable the predecessor features.
- #![cfg_attr(CONFIG_RUSTC_HAS_COERCE_POINTEE, feature(derive_coerce_pointee))] +
+ // Ensure conditional compilation based on the kernel configuration works;
+ // otherwise we may silently break things like initcall handling.

## rust/macros/module.rs ##
@@ rust/macros/module.rs: fn emit_base(&mut self, field: &str, content: &str, builtin: bool) {
{cfg}
#[doc(hidden)]
- #[cfg_attr(not(target_os = \"macos\"), link_section = \".modinfo\")]
+ #[link_section = \".modinfo\"]
- #[used]
+ #[used(compiler)]
pub static __{module}_{counter}: [u8; {length}] = *{string};
@@ rust/macros/module.rs: mod __module_init {{
+ #[used(compiler)]
static __IS_RUST_MODULE: () = ();

- static mut __MOD: ::core::mem::MaybeUninit<{type_}> =
+ static mut __MOD: Option<{type_}> = None;
@@ rust/macros/module.rs: mod __module_init {{

#[cfg(MODULE)]
@@ rust/macros/module.rs: mod __module_init {{
#[link_section = \"{initcall_section}\"]
- #[used]
+ #[used(compiler)]
- pub static __{ident}_initcall: extern \"C\" fn() ->
- ::kernel::ffi::c_int = __{ident}_init;
+ pub static __{name}_initcall: extern \"C\" fn() -> kernel::ffi::c_int = __{name}_init;

+ #[cfg(not(MODULE))]

## scripts/Makefile.build ##
@@ scripts/Makefile.build: $(obj)/%.lst: $(obj)/%.c FORCE
- # - Stable since Rust 1.82.0: feature(asm_const), feature(raw_ref_op).
- # - Stable since Rust 1.87.0: feature(asm_goto).
- # - Expected to become stable: feature(arbitrary_self_types).
-+# - To be determined: feature(used_with_arg).
- #
- # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
- # the unstable features in use.
--rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op -+rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op,used_with_arg
+ # Compile Rust sources (.rs)
+ # ---------------------------------------------------------------------------
+
+-rust_allowed_features := arbitrary_self_types,lint_reasons
++rust_allowed_features := arbitrary_self_types,lint_reasons,used_with_arg

# --out-dir is required to avoid temporaries being created by rustc in the # current working directory, which may be not accessible in the out-of-tree

---

Results of testing on various branches:

| Branch                    | Patch Apply | Build Test |
|---------------------------|-------------|------------|
| origin/linux-6.12.y       | Success     | Success    |