On 8/13/26 4:56 PM, Gary Guo wrote:
> On Thu Aug 13, 2026 at 3:15 PM BST, Petr Pavlu wrote:
>> On 8/7/26 3:26 AM, Aaron Tomlin wrote:
>>> diff --git a/rust/macros/module.rs b/rust/macros/module.rs
>>> index 06c18e207508..13353b43b38d 100644
>>> --- a/rust/macros/module.rs
>>> +++ b/rust/macros/module.rs
>>> @@ -479,6 +479,7 @@ pub(crate) fn module(info: ModuleInfo) ->
>>> Result<TokenStream> {
>>> let ident_init = format_ident!("__{ident}_init");
>>> let ident_exit = format_ident!("__{ident}_exit");
>>> let ident_initcall = format_ident!("__{ident}_initcall");
>>> + let ident_modname = format_ident!("__{ident}_modname");
>>> let initcall_section = ".initcall6.init";
>>>
>>> let global_asm = format!(
>>> @@ -590,6 +591,21 @@ pub extern "C" fn cleanup_module() {
>>> #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
>>> ::core::arch::global_asm!(#global_asm);
>>>
>>> + #[cfg(not(MODULE))]
>>> + #[repr(C)]
>>> + struct InitcallModname {
>>> + initcall_fn: extern "C" fn() -> ::kernel::ffi::c_int,
>>> + modname: *const ::kernel::ffi::c_char,
>>> + }
>>> +
>>> + #[cfg(not(MODULE))]
>>> + #[used(compiler)]
>>> + #[link_section = ".initcall.modnames"]
>>> + static #ident_modname: InitcallModname = InitcallModname {
>>
>> Can Rust directly use the C definition of initcall_modname via
>> ::kernel::bindings::initcall_modname?
>>
>>> + initcall_fn: #ident_init,
>>> + modname: #name_cstr.as_ptr().cast(),
>>
>> Can the modname string be placed in .init.rodata to match the behavior
>> on the C side?
>
> Putting strings in .init.rodata is more likely to grow the size of kernel
> because it cannot be deduplicated with other strings; the names are very
> likely
> to be in .rodata already due to it being added to sysfs when registering with
> a
> bus.
On the other hand, if these module name strings are not placed in
.init.rodata and don't get merged with an existing string in vmlinux,
some memory will be wasted after initialization completes. Built-in
drivers with device_driver::mod_name should have their names in vmlinux
but the same is not necessarily true for other modules.
For instance, my system is running openSUSE Tumbleweed with the stable
7.1.8 kernel. It has 227 built-in modules, about half of which are
drivers.
$ wc -l "/usr/lib/modules/$(uname -r)/modules.builtin"
227 /usr/lib/modules/7.1.8-1-default/modules.builtin
$ grep ^kernel/drivers "/usr/lib/modules/$(uname -r)/modules.builtin" | wc -l
125
Looking deeper, the script below runs the strings utility on vmlinux and
checks whether the name of each built-in module is already present in
the binary, at least as a suffix of another string.
On my system, the script shows that 156 modules have their names present
in vmlinux, while 71 names are missing. The total size of the present
module names is 1436 bytes, while the size of the missing module names
is 911 bytes.
This means that if .initcall.modnames places its strings in
.init.rodata, the size of the on-disk and initial kernel image should
increase by 1436+911 bytes. On the other hand, if the strings are not
placed in .init.rodata, 911 bytes will be wasted after initialization
completes.
So there is a trade-off.
--
Cheers,
Petr
#!/bin/bash
vmlinux=$(xzcat "/usr/lib/modules/$(uname -r)/vmlinux.xz" | strings)
matched=0 unmatched_modules=0 extra_bytes=0 lost_bytes=0
for file in $(cat "/usr/lib/modules/$(uname -r)/modules.builtin"); do
base=$(basename --suffix=.ko "$file" | tr '-' '_')
echo "$vmlinux" | grep -q "$base$"
ret=$?
if [ "$ret" -eq 0 ]; then
matched_modules=$((matched_modules + 1))
extra_bytes=$((extra_bytes + ${#base} + 1))
else
unmatched_modules=$((unmatched_modules + 1))
lost_bytes=$((lost_bytes + ${#base} + 1))
fi
echo $ret $base
done
echo
echo "Matched modules: $matched_modules"
echo "Extra bytes: $extra_bytes"
echo
echo "Unmatched modules: $unmatched_modules"
echo "Lost bytes: $lost_bytes"