On Mon, Sep 14, 2026 at 11:59 PM Hao Ge <[email protected]> wrote:
>
> SHF_ALLOC means, per the ELF spec, that a section occupies memory
> during process execution. Some module sections occupy memory
> outside the regular module layout, for example the percpu section
> with its per-CPU allocations. The loader currently excludes such
> a section from the layout by clearing its SHF_ALLOC, which
> overloads the flag with a loader-internal meaning.
> apply_relocations() needs a special case for the section, and
> find_sec(".data..percpu") returns different results before and
> after layout_and_allocate().
>
> Introduce SH_ENTSIZE_STANDALONE to mark sections with a separate
> allocation. The percpu section is its first user. layout_sections()
> and move_module() skip marked sections, and apply_relocations() goes
> back to testing only SHF_ALLOC. Based on a patch by Petr Pavlu [1].
>
> .data..percpu keeps SHF_ALLOC, so it would now show up under
> /sys/module/*/sections/. The section has one instance per CPU and no
> single address to report, and the entry never existed before, so
> skip it in add_sect_attrs(). add_notes_attrs() indexes its attrs[]
> array and skips it too. No functional change otherwise.

And IIUC no userspace-visible changes too, right?

>
> Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
> Reported-by: Sashiko <[email protected]>
> Link: 
> https://lore.kernel.org/all/[email protected]/ [1]
> Suggested-by: Petr Pavlu <[email protected]>
> Reviewed-by: Petr Pavlu <[email protected]>
> Cc: [email protected]
> Signed-off-by: Hao Ge <[email protected]>

Reviewed-by: Suren Baghdasaryan <[email protected]>

> ---
>  include/linux/module.h   |  2 ++
>  kernel/module/internal.h |  8 ++++++++
>  kernel/module/kallsyms.c | 13 +++----------
>  kernel/module/main.c     | 32 +++++++++++++++++---------------
>  kernel/module/sysfs.c    | 17 +++++++++++++----
>  5 files changed, 43 insertions(+), 29 deletions(-)
>
> diff --git a/include/linux/module.h b/include/linux/module.h
> index 96cc98568eea..0c6f32ddcbf2 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
> @@ -325,6 +325,8 @@ enum mod_mem_type {
>         MOD_INIT_RODATA,
>
>         MOD_MEM_NUM_TYPES,
> +
> +       MOD_STANDALONE = -2,
>         MOD_INVALID = -1,
>  };
>
> diff --git a/kernel/module/internal.h b/kernel/module/internal.h
> index 061161cc79d9..4c738074a27b 100644
> --- a/kernel/module/internal.h
> +++ b/kernel/module/internal.h
> @@ -29,6 +29,14 @@
>  #define SH_ENTSIZE_TYPE_MASK   ((1UL << SH_ENTSIZE_TYPE_BITS) - 1)
>  #define SH_ENTSIZE_OFFSET_MASK ((1UL << (BITS_PER_LONG - 
> SH_ENTSIZE_TYPE_BITS)) - 1)
>
> +/*
> + * Marker for sections with a separate allocation, which are not placed
> + * into mod->mem[].
> + */
> +#define SH_ENTSIZE_STANDALONE                                  \
> +       (((unsigned long)MOD_STANDALONE & SH_ENTSIZE_TYPE_MASK) \
> +        << SH_ENTSIZE_TYPE_SHIFT)
> +
>  /* Maximum number of characters written by module_flags() */
>  #define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4)
>
> diff --git a/kernel/module/kallsyms.c b/kernel/module/kallsyms.c
> index f23126d804b2..0dca6d40160e 100644
> --- a/kernel/module/kallsyms.c
> +++ b/kernel/module/kallsyms.c
> @@ -76,7 +76,7 @@ static char elf_type(const Elf_Sym *sym, const struct 
> load_info *info)
>  }
>
>  static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
> -                          unsigned int shnum, unsigned int pcpundx)
> +                          unsigned int shnum)
>  {
>         const Elf_Shdr *sec;
>         enum mod_mem_type type;
> @@ -86,11 +86,6 @@ static bool is_core_symbol(const Elf_Sym *src, const 
> Elf_Shdr *sechdrs,
>             !src->st_name)
>                 return false;
>
> -#ifdef CONFIG_KALLSYMS_ALL
> -       if (src->st_shndx == pcpundx)
> -               return true;
> -#endif
> -
>         sec = sechdrs + src->st_shndx;
>         type = sec->sh_entsize >> SH_ENTSIZE_TYPE_SHIFT;
>         if (!(sec->sh_flags & SHF_ALLOC)
> @@ -131,8 +126,7 @@ void layout_symtab(struct module *mod, struct load_info 
> *info)
>         /* Compute total space required for the core symbols' strtab. */
>         for (ndst = i = 0; i < nsrc; i++) {
>                 if (i == 0 || is_livepatch_module(mod) ||
> -                   is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
> -                                  info->index.pcpu)) {
> +                   is_core_symbol(src + i, info->sechdrs, 
> info->hdr->e_shnum)) {
>                         strtab_size += strlen(&info->strtab[src[i].st_name]) 
> + 1;
>                         ndst++;
>                 }
> @@ -199,8 +193,7 @@ void add_kallsyms(struct module *mod, const struct 
> load_info *info)
>         for (ndst = i = 0; i < kallsyms->num_symtab; i++) {
>                 kallsyms->typetab[i] = elf_type(src + i, info);
>                 if (i == 0 || is_livepatch_module(mod) ||
> -                   is_core_symbol(src + i, info->sechdrs, info->hdr->e_shnum,
> -                                  info->index.pcpu)) {
> +                   is_core_symbol(src + i, info->sechdrs, 
> info->hdr->e_shnum)) {
>                         ssize_t ret;
>
>                         mod->core_kallsyms.typetab[ndst] =
> diff --git a/kernel/module/main.c b/kernel/module/main.c
> index c1b34dc1e89a..ae2678ac7840 100644
> --- a/kernel/module/main.c
> +++ b/kernel/module/main.c
> @@ -1619,12 +1619,13 @@ static int apply_relocations(struct module *mod, 
> const struct load_info *info)
>
>                 /*
>                  * Don't bother with non-allocated sections.
> -                * An exception is the percpu section, which has separate 
> allocations
> -                * for individual CPUs. We relocate the percpu section in the 
> initial
> -                * ELF template and subsequently copy it to the per-CPU 
> destinations.
> +                *
> +                * Note that .data..percpu has separate allocations for
> +                * individual CPUs. We relocate the section in the
> +                * initial ELF template and subsequently copy it to the
> +                * per-CPU destinations.
>                  */
> -               if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC) &&
> -                   (!infosec || infosec != info->index.pcpu))
> +               if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
>                         continue;
>
>                 if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH)
> @@ -1715,7 +1716,7 @@ static void __layout_sections(struct module *mod, 
> struct load_info *info, bool i
>
>                         if ((s->sh_flags & masks[m][0]) != masks[m][0]
>                             || (s->sh_flags & masks[m][1])
> -                           || s->sh_entsize != ~0UL
> +                           || s->sh_entsize != ~0UL /* offset or standalone 
> */
>                             || is_init != module_init_layout_section(sname))
>                                 continue;
>
> @@ -1745,16 +1746,10 @@ static void __layout_sections(struct module *mod, 
> struct load_info *info, bool i
>  /*
>   * Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
>   * might -- code, read-only data, read-write data, small data.  Tally
> - * sizes, and place the offsets into sh_entsize fields: high bit means it
> - * belongs in init.
> + * sizes, and place the offsets into sh_entsize fields.
>   */
>  static void layout_sections(struct module *mod, struct load_info *info)
>  {
> -       unsigned int i;
> -
> -       for (i = 0; i < info->hdr->e_shnum; i++)
> -               info->sechdrs[i].sh_entsize = ~0UL;
> -
>         pr_debug("Core section allocation order for %s:\n", mod->name);
>         __layout_sections(mod, info, false);
>
> @@ -2822,7 +2817,8 @@ static int move_module(struct module *mod, struct 
> load_info *info)
>                 Elf_Shdr *shdr = &info->sechdrs[i];
>                 const char *sname;
>
> -               if (!(shdr->sh_flags & SHF_ALLOC))
> +               if (!(shdr->sh_flags & SHF_ALLOC)
> +                   || shdr->sh_entsize == SH_ENTSIZE_STANDALONE)
>                         continue;
>
>                 sname = info->secstrings + shdr->sh_name;
> @@ -2954,6 +2950,7 @@ core_param(module_blacklist, module_blacklist, charp, 
> 0400);
>  static struct module *layout_and_allocate(struct load_info *info, int flags)
>  {
>         struct module *mod;
> +       unsigned int i;
>         int err;
>
>         /* Allow arches to frob section contents and sizes.  */
> @@ -2967,8 +2964,13 @@ static struct module *layout_and_allocate(struct 
> load_info *info, int flags)
>         if (err < 0)
>                 return ERR_PTR(err);
>
> +       /* Repurpose sh_entsize to track where each section is allocated. */
> +       for (i = 0; i < info->hdr->e_shnum; i++)
> +               info->sechdrs[i].sh_entsize = ~0UL;
> +
>         /* We will do a special allocation for per-cpu sections later. */
> -       info->sechdrs[info->index.pcpu].sh_flags &= ~(unsigned long)SHF_ALLOC;
> +       if (info->index.pcpu)
> +               info->sechdrs[info->index.pcpu].sh_entsize = 
> SH_ENTSIZE_STANDALONE;
>
>         /*
>          * Mark relevant sections as SHF_RO_AFTER_INIT so layout_sections() 
> can
> diff --git a/kernel/module/sysfs.c b/kernel/module/sysfs.c
> index 01c65d608873..15c6baeb39c4 100644
> --- a/kernel/module/sysfs.c
> +++ b/kernel/module/sysfs.c
> @@ -62,6 +62,15 @@ static void free_sect_attrs(struct module_sect_attrs 
> *sect_attrs)
>         kfree(sect_attrs);
>  }
>
> +/*
> + * .data..percpu has a separate allocation per CPU and no single
> + * address to report.
> + */
> +static bool sect_visible(const struct load_info *info, unsigned int i)
> +{
> +       return !sect_empty(&info->sechdrs[i]) && i != info->index.pcpu;
> +}
> +
>  static int add_sect_attrs(struct module *mod, const struct load_info *info)
>  {
>         struct module_sect_attrs *sect_attrs;
> @@ -72,7 +81,7 @@ static int add_sect_attrs(struct module *mod, const struct 
> load_info *info)
>
>         /* Count loaded sections and allocate structures */
>         for (i = 0; i < info->hdr->e_shnum; i++)
> -               if (!sect_empty(&info->sechdrs[i]))
> +               if (sect_visible(info, i))
>                         nloaded++;
>         sect_attrs = kzalloc_flex(*sect_attrs, attrs, nloaded);
>         if (!sect_attrs)
> @@ -92,7 +101,7 @@ static int add_sect_attrs(struct module *mod, const struct 
> load_info *info)
>         for (i = 0; i < info->hdr->e_shnum; i++) {
>                 Elf_Shdr *sec = &info->sechdrs[i];
>
> -               if (sect_empty(sec))
> +               if (!sect_visible(info, i))
>                         continue;
>                 sysfs_bin_attr_init(sattr);
>                 sattr->attr.name =
> @@ -159,7 +168,7 @@ static int add_notes_attrs(struct module *mod, const 
> struct load_info *info)
>         /* Count notes sections and allocate structures.  */
>         notes = 0;
>         for (i = 0; i < info->hdr->e_shnum; i++)
> -               if (!sect_empty(&info->sechdrs[i]) &&
> +               if (sect_visible(info, i) &&
>                     info->sechdrs[i].sh_type == SHT_NOTE)
>                         ++notes;
>
> @@ -181,7 +190,7 @@ static int add_notes_attrs(struct module *mod, const 
> struct load_info *info)
>
>         nattr = &notes_attrs->attrs[0];
>         for (loaded = i = 0; i < info->hdr->e_shnum; ++i) {
> -               if (sect_empty(&info->sechdrs[i]))
> +               if (!sect_visible(info, i))
>                         continue;
>                 if (info->sechdrs[i].sh_type == SHT_NOTE) {
>                         sysfs_bin_attr_init(nattr);
> --
> 2.25.1
>

Reply via email to