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 is now exported under
/sys/module/*/sections/. Report the boot CPU instance of the
module's per-CPU area there, as __is_module_percpu_address() does.
No functional change otherwise.
Fixes: 4835f747d3ed ("alloc_tag: support for page allocation tag compression")
Reported-by: Sashiko <[email protected]>
Link:
https://lore.kernel.org/all/[email protected]/ [1]
Cc: [email protected]
Signed-off-by: Hao Ge <[email protected]>
---
Documentation/ABI/testing/sysfs-module | 8 ++++++
include/linux/module.h | 2 ++
kernel/module/internal.h | 8 ++++++
kernel/module/kallsyms.c | 13 +++------
kernel/module/main.c | 37 +++++++++++++++-----------
5 files changed, 43 insertions(+), 25 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-module
b/Documentation/ABI/testing/sysfs-module
index d5b7d19bd310..65e152c6fa58 100644
--- a/Documentation/ABI/testing/sysfs-module
+++ b/Documentation/ABI/testing/sysfs-module
@@ -57,6 +57,14 @@ Description: List of symbol namespaces imported by this
module via
This file only exists for modules that import at least one
namespace.
+What: /sys/module/*/sections/<section>
+Date: June 2005
+KernelVersion: 2.6.12
+Contact: [email protected]
+Description: The memory address of the given loaded section of the
+ module. .data..percpu has one instance per CPU; the
+ address reported is the instance of the boot CPU.
+
What: /sys/module/*/taint
Date: Jan 2012
KernelVersion: 3.3
diff --git a/include/linux/module.h b/include/linux/module.h
index 7566815fabbe..33548daa31a3 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 0fc11e45df9b..49190deae61e 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 c32f1d370b73..20b85148ad72 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1620,12 +1620,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)
@@ -1716,7 +1717,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;
@@ -1746,16 +1747,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);
@@ -2811,7 +2806,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;
@@ -2943,6 +2939,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. */
@@ -2956,8 +2953,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
@@ -3520,6 +3522,11 @@ static int load_module(struct load_info *info, const
char __user *uargs,
if (err < 0)
goto free_modinfo;
+ /* The percpu ELF section is a template; report the boot CPU instance.
*/
+ if (info->index.pcpu)
+ info->sechdrs[info->index.pcpu].sh_addr =
+ (unsigned long)per_cpu_ptr(mod->percpu,
get_boot_cpu_id());
+
flush_module_icache(mod);
/* Now copy in args */
--
2.25.1