Re: [PATCH] rust: export zerocopy symbols to modules

From: Gary Guo

Date: Wed Jul 08 2026 - 07:51:49 EST


On Wed Jul 8, 2026 at 12:34 PM BST, Gary Guo wrote:
> On Wed Jul 8, 2026 at 11:37 AM BST, Alexandre Courbot wrote:
>> 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
>

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.

diff --git a/rust/Makefile b/rust/Makefile
index a870d1616c71..98c6086342a7 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -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
diff --git a/rust/zerocopy/src/layout.rs b/rust/zerocopy/src/layout.rs
index 6015d0f2de52..1870363adf10 100644
--- a/rust/zerocopy/src/layout.rs
+++ b/rust/zerocopy/src/layout.rs
@@ -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 },
diff --git a/rust/zerocopy/src/util/mod.rs b/rust/zerocopy/src/util/mod.rs
index d6d4c6c2fcd9..ceffe8b5510d 100644
--- a/rust/zerocopy/src/util/mod.rs
+++ b/rust/zerocopy/src/util/mod.rs
@@ -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