On Thu, Sep 18, 2025 at 10:05:51AM +0200, Alexey Gladkov wrote:
> For some modules, modalias is generated using the modpost utility and
> the section is added to the module file.
> 
> When a module is added inside vmlinux, modpost does not generate
> modalias for such modules and the information is lost.
> 
> As a result kmod (which uses modules.builtin.modinfo in userspace)
> cannot determine that modalias is handled by a builtin kernel module.
> 
> $ cat /sys/devices/pci0000:00/0000:00:14.0/modalias
> pci:v00008086d0000A36Dsv00001043sd00008694bc0Csc03i30
> 
> $ modinfo xhci_pci
> name:           xhci_pci
> filename:       (builtin)
> license:        GPL
> file:           drivers/usb/host/xhci-pci
> description:    xHCI PCI Host Controller Driver
> 
> Missing modalias "pci:v*d*sv*sd*bc0Csc03i30*" which will be generated by
> modpost if the module is built separately.
> 
> To fix this it is necessary to generate the same modalias for vmlinux as
> for the individual modules. Fortunately '.vmlinux.export.o' is already
> generated from which '.modinfo' can be extracted in the same way as for
> vmlinux.o.

Hi -

This patch broke RISC-V builds for me. During the final objcopy where the new
symbols are supposed to be stripped, an error occurs producing lots of error
messages similar to this one:

riscv64-linux-gnu-objcopy: not stripping symbol `__mod_device_table__...'
because it is named in a relocation

It does not occur using defconfig, but I was able to bisect my way to this
commit and then reduce my config delta w.r.t defconfig until I landed on:

cat > .config <<'EOF'
CONFIG_RELOCATABLE=y
CONFIG_KASAN=y
EOF
ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- make olddefconfig
ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- make -j $(nproc)
...
  LD      vmlinux.unstripped
  NM      System.map
  SORTTAB vmlinux.unstripped
  CHKREL  vmlinux.unstripped
  OBJCOPY vmlinux
  OBJCOPY modules.builtin.modinfo
  GEN     modules.builtin
riscv64-linux-gnu-objcopy: not stripping symbol `<long symbol name>'
because it is named in a relocation
<repeats with different symbol names about a dozen times>
make[3]: *** [scripts/Makefile.vmlinux:97: vmlinux] Error 1
make[3]: *** Deleting file 'vmlinux'
make[2]: *** [Makefile:1242: vmlinux] Error 2
make[1]: *** [/tmp/linux/Makefile:369: __build_one_by_one] Error 2
make: *** [Makefile:248: __sub-make] Error 2

I confirmed that reverting this commit fixes the issue.

> 
> Signed-off-by: Masahiro Yamada <[email protected]>
> Signed-off-by: Alexey Gladkov <[email protected]>
> Tested-by: Stephen Rothwell <[email protected]>
> ---
>  include/linux/module.h   |  4 ----
>  scripts/Makefile.vmlinux |  4 +++-
>  scripts/mksysmap         |  3 +++
>  scripts/mod/file2alias.c | 19 ++++++++++++++++++-
>  scripts/mod/modpost.c    | 15 +++++++++++++++
>  scripts/mod/modpost.h    |  2 ++
>  6 files changed, 41 insertions(+), 6 deletions(-)
> 
> diff --git a/include/linux/module.h b/include/linux/module.h
> index e31ee29fac6b7..e135cc79aceea 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
> @@ -256,14 +256,10 @@ struct module_kobject 
> *lookup_or_create_module_kobject(const char *name);
>       __PASTE(type,                   \
>       __PASTE(__, name)))))
>  
> -#ifdef MODULE
>  /* Creates an alias so file2alias.c can find device table. */
>  #define MODULE_DEVICE_TABLE(type, name)                                      
> \
>  static typeof(name) __mod_device_table(type, name)                   \
>    __attribute__ ((used, alias(__stringify(name))))
> -#else  /* !MODULE */
> -#define MODULE_DEVICE_TABLE(type, name)
> -#endif
>  
>  /* Version of form [<epoch>:]<version>[-<extra-version>].
>   * Or for CVS/RCS ID version, everything but the number is stripped.
> diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
> index ce79461714979..1e5e37aadcd05 100644
> --- a/scripts/Makefile.vmlinux
> +++ b/scripts/Makefile.vmlinux
> @@ -89,11 +89,13 @@ endif
>  remove-section-y                                   := .modinfo
>  remove-section-$(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS) += '.rel*'
>  
> +remove-symbols := -w --strip-symbol='__mod_device_table__*'
> +
>  # To avoid warnings: "empty loadable segment detected at ..." from GNU 
> objcopy,
>  # it is necessary to remove the PT_LOAD flag from the segment.
>  quiet_cmd_strip_relocs = OBJCOPY $@
>        cmd_strip_relocs = $(OBJCOPY) $(patsubst %,--set-section-flags 
> %=noload,$(remove-section-y)) $< $@; \
> -                         $(OBJCOPY) $(addprefix 
> --remove-section=,$(remove-section-y)) $@
> +                         $(OBJCOPY) $(addprefix 
> --remove-section=,$(remove-section-y)) $(remove-symbols) $@
>  
>  targets += vmlinux
>  vmlinux: vmlinux.unstripped FORCE
> diff --git a/scripts/mksysmap b/scripts/mksysmap
> index a607a0059d119..c4531eacde202 100755
> --- a/scripts/mksysmap
> +++ b/scripts/mksysmap
> @@ -59,6 +59,9 @@
>  # EXPORT_SYMBOL (namespace)
>  / __kstrtabns_/d
>  
> +# MODULE_DEVICE_TABLE (symbol name)
> +/ __mod_device_table__/d
> +
>  # ---------------------------------------------------------------------------
>  # Ignored suffixes
>  #  (do not forget '$' after each pattern)
> diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
> index 1260bc2287fba..7da9735e7ab3e 100644
> --- a/scripts/mod/file2alias.c
> +++ b/scripts/mod/file2alias.c
> @@ -1477,7 +1477,7 @@ void handle_moddevtable(struct module *mod, struct 
> elf_info *info,
>       void *symval;
>       char *zeros = NULL;
>       const char *type, *name, *modname;
> -     size_t typelen;
> +     size_t typelen, modnamelen;
>       static const char *prefix = "__mod_device_table__";
>  
>       /* We're looking for a section relative symbol */
> @@ -1500,6 +1500,7 @@ void handle_moddevtable(struct module *mod, struct 
> elf_info *info,
>       type = strstr(modname, "__");
>       if (!type)
>               return;
> +     modnamelen = type - modname;
>       type += strlen("__");
>  
>       name = strstr(type, "__");
> @@ -1526,5 +1527,21 @@ void handle_moddevtable(struct module *mod, struct 
> elf_info *info,
>               }
>       }
>  
> +     if (mod->is_vmlinux) {
> +             struct module_alias *alias;
> +
> +             /*
> +              * If this is vmlinux, record the name of the builtin module.
> +              * Traverse the linked list in the reverse order, and set the
> +              * builtin_modname unless it has already been set in the
> +              * previous call.
> +              */
> +             list_for_each_entry_reverse(alias, &mod->aliases, node) {
> +                     if (alias->builtin_modname)
> +                             break;
> +                     alias->builtin_modname = xstrndup(modname, modnamelen);
> +             }
> +     }
> +
>       free(zeros);
>  }
> diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
> index 5ca7c268294eb..47c8aa2a69392 100644
> --- a/scripts/mod/modpost.c
> +++ b/scripts/mod/modpost.c
> @@ -2067,11 +2067,26 @@ static void write_if_changed(struct buffer *b, const 
> char *fname)
>  static void write_vmlinux_export_c_file(struct module *mod)
>  {
>       struct buffer buf = { };
> +     struct module_alias *alias, *next;
>  
>       buf_printf(&buf,
>                  "#include <linux/export-internal.h>\n");
>  
>       add_exported_symbols(&buf, mod);
> +
> +     buf_printf(&buf,
> +                "#include <linux/module.h>\n"
> +                "#undef __MODULE_INFO_PREFIX\n"
> +                "#define __MODULE_INFO_PREFIX\n");
> +
> +     list_for_each_entry_safe(alias, next, &mod->aliases, node) {
> +             buf_printf(&buf, "MODULE_INFO(%s.alias, \"%s\");\n",
> +                        alias->builtin_modname, alias->str);
> +             list_del(&alias->node);
> +             free(alias->builtin_modname);
> +             free(alias);
> +     }
> +
>       write_if_changed(&buf, ".vmlinux.export.c");
>       free(buf.p);
>  }
> diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
> index 9133e4c3803f0..2aecb8f25c87e 100644
> --- a/scripts/mod/modpost.h
> +++ b/scripts/mod/modpost.h
> @@ -99,10 +99,12 @@ buf_write(struct buffer *buf, const char *s, int len);
>   * struct module_alias - auto-generated MODULE_ALIAS()
>   *
>   * @node: linked to module::aliases
> + * @modname: name of the builtin module (only for vmlinux)
>   * @str: a string for MODULE_ALIAS()
>   */
>  struct module_alias {
>       struct list_head node;
> +     char *builtin_modname;
>       char str[];
>  };
>  
> -- 
> 2.51.0
> 


Reply via email to