Skip to contentSkip to conversation

rust: export zerocopy symbols to modules

PATCH In review Alexandre Courbot 3 participants 6 messages Jul 8 1 message not in the archive Rust

lore
Share & tools
lore

Alexandre Courbot

author reviewer
#0 lore raw

PATCH 1/1 rust: export zerocopy symbols to modules

Non-generic, non-inlined functions of the zerocopy crate are only codegen'd into zerocopy.o, with their symbols currently not exported, making them unavailable to loadable modules.

This can break e.g. nova-core built as a module when CONFIG_CC_OPTIMIZE_FOR_SIZE=y:

ERROR: modpost: "_RNvMNtCs5wX7wwEUCR9_8zerocopy6layoutNtB2_8SizeInfo24try_to_nonzero_elem_size" [drivers/gpu/nova-core.ko] undefined!
ERROR: modpost: "_RNvNtCs5wX7wwEUCR9_8zerocopy4util18padding_needed_for" [drivers/gpu/nova-core.ko] undefined!

Fix this by generating and including exports for zerocopy.o, similarly to what is already done for kernel.o. All defined symbols (~12 in total) are exported since the compiler gives no guarantee as to which functions are codegen'd.

---
My CONFIG_CC_OPTIMIZE_FOR_SIZE=y config is uncovering the most interesting bugs. :) This one was revealed after applying [1].

I am not a build system expert at all, so be aware that the patch is mostly mimicking what I am seeing for the kernel crate and there might be a better way.

[1] https://lore.kernel.org/[email protected]

---
 rust/Makefile  | 4 ++++
 rust/exports.c | 1 +
 2 files changed, 5 insertions(+)
rust/Makefile +4 -0
index a870d1616c71..65170c6d31a4 100644
@@ -7,6 +7,7 @@ obj-$(CONFIG_RUST) += core.o compiler_builtins.o ffi.o
 always-$(CONFIG_RUST) += exports_core_generated.h
 
 obj-$(CONFIG_RUST) += zerocopy.o
+always-$(CONFIG_RUST) += exports_zerocopy_generated.h
 
 ifdef CONFIG_RUST_INLINE_HELPERS
 always-$(CONFIG_RUST) += helpers/helpers.bc helpers/helpers_module.bc
@@ -566,6 +567,9 @@ $(obj)/exports_bindings_generated.h: $(obj)/bindings.o FORCE
 $(obj)/exports_kernel_generated.h: $(obj)/kernel.o FORCE
 	$(call if_changed,exports)
 
+$(obj)/exports_zerocopy_generated.h: $(obj)/zerocopy.o FORCE
+	$(call if_changed,exports)
+
 quiet_cmd_rustc_procmacrolibrary = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) PL $@
       cmd_rustc_procmacrolibrary = \
 	$(rustc_target_envs) \
rust/exports.c +1 -0
index 1b52460b0f4e..a9d6be8e8485 100644
@@ -18,6 +18,7 @@
 #include "exports_core_generated.h"
 #include "exports_bindings_generated.h"
 #include "exports_kernel_generated.h"
+#include "exports_zerocopy_generated.h"
 
 #ifndef CONFIG_RUST_INLINE_HELPERS
 #include "exports_helpers_generated.h"

---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260708-zerocopy-export-e6d29c42386c

Best regards,

Signature · 1 line
Alexandre Courbot <[email protected]>
#1 lore raw
Quoted 4 lines · Alexandre Courbot

Non-generic, non-inlined functions of the zerocopy crate are only codegen'd into zerocopy.o, with their symbols currently not exported, making them unavailable to loadable modules.

Ah, in that case, yeah, we definitely need this.

Quoted 2 lines

My CONFIG_CC_OPTIMIZE_FOR_SIZE=y config is uncovering the most interesting bugs. :) This one was revealed after applying [1].

Yeah, it is a bit painful. I will add it to my set.

Quoted 1 line

I am not a build system expert at all, so be aware that the patch is

No one really is... :) (Unless you are named Masahiro!)

I think I will route this one via rust-fixes.

Cheers,
Miguel

reviewer
#2 lore raw
In reply to Alexandre Courbot
Quoted 9 lines · Alexandre Courbot

Non-generic, non-inlined functions of the zerocopy crate are only codegen'd into zerocopy.o, with their symbols currently not exported, making them unavailable to loadable modules.

This can break e.g. nova-core built as a module when CONFIG_CC_OPTIMIZE_FOR_SIZE=y:

ERROR: modpost: "_RNvMNtCs5wX7wwEUCR9_8zerocopy6layoutNtB2_8SizeInfo24try_to_nonzero_elem_size" [drivers/gpu/nova-core.ko] undefined!
ERROR: modpost: "_RNvNtCs5wX7wwEUCR9_8zerocopy4util18padding_needed_for" [drivers/gpu/nova-core.ko] undefined!

It looks to me that this should be done by fixing zerocopy.

Both these functions should be marked as #[inline]. I would expect zerocopy.o to be having no .text.

Instead of exporting this, I would vouch for going to another extreme -- completely leave zerocopy.o unlinked, so we catch cases where code is actually generated.

Best,
Gary

Quoted 20 lines

Fix this by generating and including exports for zerocopy.o, similarly to what is already done for kernel.o. All defined symbols (~12 in total) are exported since the compiler gives no guarantee as to which functions are codegen'd.

---
My CONFIG_CC_OPTIMIZE_FOR_SIZE=y config is uncovering the most interesting bugs. :) This one was revealed after applying [1].

I am not a build system expert at all, so be aware that the patch is mostly mimicking what I am seeing for the kernel crate and there might be a better way.

[1] https://lore.kernel.org/[email protected]

---
 rust/Makefile  | 4 ++++
 rust/exports.c | 1 +
 2 files changed, 5 insertions(+)
#3 lore raw
Quoted 9 lines · Gary Guo

It looks to me that this should be done by fixing zerocopy.

Both these functions should be marked as #[inline]. I would expect zerocopy.o to be having no .text.

Instead of exporting this, I would vouch for going to another extreme -- completely leave zerocopy.o unlinked, so we catch cases where code is actually generated.

Cc'ing Joshua and Jack -- context here:

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

Cheers,
Miguel

A message that is not in the archive (sent off-list or not yet received): CAPvrYMF3L0x5PBJk2TnSPyi0Lci2H0vGBu6QS_Rm4XFQtTV6mw@mail.gmail.com

reviewer
#6 lore raw
In reply to Gary Guo
Quoted 16 lines · Gary Guo
Quoted 9 lines · Alexandre Courbot

Non-generic, non-inlined functions of the zerocopy crate are only codegen'd into zerocopy.o, with their symbols currently not exported, making them unavailable to loadable modules.

This can break e.g. nova-core built as a module when CONFIG_CC_OPTIMIZE_FOR_SIZE=y:

ERROR: modpost: "_RNvMNtCs5wX7wwEUCR9_8zerocopy6layoutNtB2_8SizeInfo24try_to_nonzero_elem_size" [drivers/gpu/nova-core.ko] undefined!
ERROR: modpost: "_RNvNtCs5wX7wwEUCR9_8zerocopy4util18padding_needed_for" [drivers/gpu/nova-core.ko] undefined!

It looks to me that this should be done by fixing zerocopy.

Both these functions should be marked as #[inline]. I would expect zerocopy.o to be having no .text.

Quoted 7 lines

Instead of exporting this, I would vouch for going to another extreme -- completely leave zerocopy.o unlinked, so we catch cases where code is actually generated.

Best,
Gary

This should do the trick. The only other unexported symbols are f32_ext and f64_ext, which we never use. We can mark them as #[inline(always)] as well, but figuring out how to compile them out completely might be better.

rust/Makefile +1 -1
index a870d1616c71..98c6086342a7 100644
@@ -6,7 +6,7 @@ rustdoc_output := $(objtree)/Documentation/output/rust/rustdoc
 obj-$(CONFIG_RUST) += core.o compiler_builtins.o ffi.o
 always-$(CONFIG_RUST) += exports_core_generated.h
 
-obj-$(CONFIG_RUST) += zerocopy.o
+always-$(CONFIG_RUST) += zerocopy.o
 
 ifdef CONFIG_RUST_INLINE_HELPERS
 always-$(CONFIG_RUST) += helpers/helpers.bc helpers/helpers_module.bc
rust/zerocopy/src/layout.rs +1 -0
index 6015d0f2de52..1870363adf10 100644
@@ -71,6 +71,7 @@ impl SizeInfo {
     /// Attempts to create a `SizeInfo` from `Self` in which `elem_size` is a
     /// `NonZeroUsize`. If `elem_size` is 0, returns `None`.
     #[allow(unused)]
+    #[inline(always)]
     const fn try_to_nonzero_elem_size(&self) -> Option<SizeInfo<NonZeroUsize>> {
         Some(match *self {
             SizeInfo::Sized { size } => SizeInfo::Sized { size },
rust/zerocopy/src/util/mod.rs +3 -0
index d6d4c6c2fcd9..ceffe8b5510d 100644
@@ -150,6 +150,7 @@ pub(crate) fn validate_aligned_to<T: AsAddress, U>(t: T) -> Result<(), Alignment
     // Ensures that we add the minimum required padding.
     kani::ensures(|&p| p < align.get()),
 )]
+#[inline(always)]
 pub(crate) const fn padding_needed_for(len: usize, align: NonZeroUsize) -> usize {
     #[cfg(kani)]
     #[kani::proof_for_contract(padding_needed_for)]
@@ -251,6 +252,7 @@ fn proof() {
     n & mask
 }
 
+#[inline(always)]
 pub(crate) const fn max(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize {
     if a.get() < b.get() {
         b
@@ -259,6 +261,7 @@ pub(crate) const fn max(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize {
     }
 }
 
+#[inline(always)]
 pub(crate) const fn min(a: NonZeroUsize, b: NonZeroUsize) -> NonZeroUsize {
     if a.get() > b.get() {
         b