Commit 3ba2c3ff authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'modules-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux

Pull modules updates from Luis Chamberlain:
 "Tux gets for xmas an improvement to the average lookup performance of
  kallsyms_lookup_name() by 715x thanks to the work by Zhen Lei, which
  upgraded our old implementation from being O(n) to O(log(n)), while
  also retaining the old implementation support on /proc/kallsyms.

  The only penalty was increasing the memory footprint by 3 *
  kallsyms_num_syms. Folks who want to improve this further now also
  have a dedicated selftest facility through KALLSYMS_SELFTEST.

  Stephen Boyd added zstd in-kernel decompression support, but the only
  users of this would be folks using the load-pin LSM because otherwise
  we do module decompression in userspace.

  The only other thing with mentioning is a minor boot time optimization
  by Rasmus Villemoes which deferes param_sysfs_init() to late init. The
  rest is cleanups and minor fixes"

* tag 'modules-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux:
  livepatch: Call klp_match_callback() in klp_find_callback() to avoid code duplication
  module/decompress: Support zstd in-kernel decompression
  kallsyms: Remove unneeded semicolon
  kallsyms: Add self-test facility
  livepatch: Use kallsyms_on_each_match_symbol() to improve performance
  kallsyms: Add helper kallsyms_on_each_match_symbol()
  kallsyms: Reduce the memory occupied by kallsyms_seqs_of_names[]
  kallsyms: Correctly sequence symbols when CONFIG_LTO_CLANG=y
  kallsyms: Improve the performance of kallsyms_lookup_name()
  scripts/kallsyms: rename build_initial_tok_table()
  module: Fix NULL vs IS_ERR checking for module_get_next_page
  kernel/params.c: defer most of param_sysfs_init() to late_initcall time
  module: Remove unused macros module_addr_min/max
  module: remove redundant module_sysfs_initialized variable
parents 0015edd6 4f1354d5
...@@ -66,9 +66,12 @@ static inline void *dereference_symbol_descriptor(void *ptr) ...@@ -66,9 +66,12 @@ static inline void *dereference_symbol_descriptor(void *ptr)
} }
#ifdef CONFIG_KALLSYMS #ifdef CONFIG_KALLSYMS
unsigned long kallsyms_sym_address(int idx);
int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
unsigned long), unsigned long),
void *data); void *data);
int kallsyms_on_each_match_symbol(int (*fn)(void *, unsigned long),
const char *name, void *data);
/* Lookup the address for a symbol. Returns 0 if not found. */ /* Lookup the address for a symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name); unsigned long kallsyms_lookup_name(const char *name);
...@@ -168,6 +171,12 @@ static inline int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct ...@@ -168,6 +171,12 @@ static inline int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline int kallsyms_on_each_match_symbol(int (*fn)(void *, unsigned long),
const char *name, void *data)
{
return -EOPNOTSUPP;
}
#endif /*CONFIG_KALLSYMS*/ #endif /*CONFIG_KALLSYMS*/
static inline void print_ip_sym(const char *loglvl, unsigned long ip) static inline void print_ip_sym(const char *loglvl, unsigned long ip)
......
...@@ -827,7 +827,6 @@ void *dereference_module_function_descriptor(struct module *mod, void *ptr) ...@@ -827,7 +827,6 @@ void *dereference_module_function_descriptor(struct module *mod, void *ptr)
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS
extern struct kset *module_kset; extern struct kset *module_kset;
extern struct kobj_type module_ktype; extern struct kobj_type module_ktype;
extern int module_sysfs_initialized;
#endif /* CONFIG_SYSFS */ #endif /* CONFIG_SYSFS */
#define symbol_request(x) try_then_request_module(symbol_get(x), "symbol:" #x) #define symbol_request(x) try_then_request_module(symbol_get(x), "symbol:" #x)
......
...@@ -1723,6 +1723,19 @@ config KALLSYMS ...@@ -1723,6 +1723,19 @@ config KALLSYMS
symbolic stack backtraces. This increases the size of the kernel symbolic stack backtraces. This increases the size of the kernel
somewhat, as all symbols have to be loaded into the kernel image. somewhat, as all symbols have to be loaded into the kernel image.
config KALLSYMS_SELFTEST
bool "Test the basic functions and performance of kallsyms"
depends on KALLSYMS
default n
help
Test the basic functions and performance of some interfaces, such as
kallsyms_lookup_name. It also calculates the compression rate of the
kallsyms compression algorithm for the current symbol set.
Start self-test automatically after system startup. Suggest executing
"dmesg | grep kallsyms_selftest" to collect test results. "finish" is
displayed in the last line, indicating that the test is complete.
config KALLSYMS_ALL config KALLSYMS_ALL
bool "Include all symbols in kallsyms" bool "Include all symbols in kallsyms"
depends on DEBUG_KERNEL && KALLSYMS depends on DEBUG_KERNEL && KALLSYMS
......
...@@ -69,6 +69,7 @@ endif ...@@ -69,6 +69,7 @@ endif
obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULE_SIG_FORMAT) += module_signature.o obj-$(CONFIG_MODULE_SIG_FORMAT) += module_signature.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_KALLSYMS_SELFTEST) += kallsyms_selftest.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_CRASH_CORE) += crash_core.o obj-$(CONFIG_CRASH_CORE) += crash_core.o
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
......
...@@ -146,7 +146,7 @@ static unsigned int get_symbol_offset(unsigned long pos) ...@@ -146,7 +146,7 @@ static unsigned int get_symbol_offset(unsigned long pos)
return name - kallsyms_names; return name - kallsyms_names;
} }
static unsigned long kallsyms_sym_address(int idx) unsigned long kallsyms_sym_address(int idx)
{ {
if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE)) if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
return kallsyms_addresses[idx]; return kallsyms_addresses[idx];
...@@ -187,26 +187,100 @@ static bool cleanup_symbol_name(char *s) ...@@ -187,26 +187,100 @@ static bool cleanup_symbol_name(char *s)
return false; return false;
} }
static int compare_symbol_name(const char *name, char *namebuf)
{
int ret;
ret = strcmp(name, namebuf);
if (!ret)
return ret;
if (cleanup_symbol_name(namebuf) && !strcmp(name, namebuf))
return 0;
return ret;
}
static unsigned int get_symbol_seq(int index)
{
unsigned int i, seq = 0;
for (i = 0; i < 3; i++)
seq = (seq << 8) | kallsyms_seqs_of_names[3 * index + i];
return seq;
}
static int kallsyms_lookup_names(const char *name,
unsigned int *start,
unsigned int *end)
{
int ret;
int low, mid, high;
unsigned int seq, off;
char namebuf[KSYM_NAME_LEN];
low = 0;
high = kallsyms_num_syms - 1;
while (low <= high) {
mid = low + (high - low) / 2;
seq = get_symbol_seq(mid);
off = get_symbol_offset(seq);
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
ret = compare_symbol_name(name, namebuf);
if (ret > 0)
low = mid + 1;
else if (ret < 0)
high = mid - 1;
else
break;
}
if (low > high)
return -ESRCH;
low = mid;
while (low) {
seq = get_symbol_seq(low - 1);
off = get_symbol_offset(seq);
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
if (compare_symbol_name(name, namebuf))
break;
low--;
}
*start = low;
if (end) {
high = mid;
while (high < kallsyms_num_syms - 1) {
seq = get_symbol_seq(high + 1);
off = get_symbol_offset(seq);
kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
if (compare_symbol_name(name, namebuf))
break;
high++;
}
*end = high;
}
return 0;
}
/* Lookup the address for this symbol. Returns 0 if not found. */ /* Lookup the address for this symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name) unsigned long kallsyms_lookup_name(const char *name)
{ {
char namebuf[KSYM_NAME_LEN]; int ret;
unsigned long i; unsigned int i;
unsigned int off;
/* Skip the search for empty string. */ /* Skip the search for empty string. */
if (!*name) if (!*name)
return 0; return 0;
for (i = 0, off = 0; i < kallsyms_num_syms; i++) { ret = kallsyms_lookup_names(name, &i, NULL);
off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); if (!ret)
return kallsyms_sym_address(get_symbol_seq(i));
if (strcmp(namebuf, name) == 0)
return kallsyms_sym_address(i);
if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0)
return kallsyms_sym_address(i);
}
return module_kallsyms_lookup_name(name); return module_kallsyms_lookup_name(name);
} }
...@@ -233,6 +307,24 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, ...@@ -233,6 +307,24 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
return 0; return 0;
} }
int kallsyms_on_each_match_symbol(int (*fn)(void *, unsigned long),
const char *name, void *data)
{
int ret;
unsigned int i, start, end;
ret = kallsyms_lookup_names(name, &start, &end);
if (ret)
return 0;
for (i = start; !ret && i <= end; i++) {
ret = fn(data, kallsyms_sym_address(get_symbol_seq(i)));
cond_resched();
}
return ret;
}
static unsigned long get_symbol_pos(unsigned long addr, static unsigned long get_symbol_pos(unsigned long addr,
unsigned long *symbolsize, unsigned long *symbolsize,
unsigned long *offset) unsigned long *offset)
......
...@@ -26,5 +26,6 @@ extern const char kallsyms_token_table[] __weak; ...@@ -26,5 +26,6 @@ extern const char kallsyms_token_table[] __weak;
extern const u16 kallsyms_token_index[] __weak; extern const u16 kallsyms_token_index[] __weak;
extern const unsigned int kallsyms_markers[] __weak; extern const unsigned int kallsyms_markers[] __weak;
extern const u8 kallsyms_seqs_of_names[] __weak;
#endif // LINUX_KALLSYMS_INTERNAL_H_ #endif // LINUX_KALLSYMS_INTERNAL_H_
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Test the function and performance of kallsyms
*
* Copyright (C) Huawei Technologies Co., Ltd., 2022
*
* Authors: Zhen Lei <thunder.leizhen@huawei.com> Huawei
*/
#define pr_fmt(fmt) "kallsyms_selftest: " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/random.h>
#include <linux/sched/clock.h>
#include <linux/kthread.h>
#include <linux/vmalloc.h>
#include "kallsyms_internal.h"
#include "kallsyms_selftest.h"
#define MAX_NUM_OF_RECORDS 64
struct test_stat {
int min;
int max;
int save_cnt;
int real_cnt;
int perf;
u64 sum;
char *name;
unsigned long addr;
unsigned long addrs[MAX_NUM_OF_RECORDS];
};
struct test_item {
char *name;
unsigned long addr;
};
#define ITEM_FUNC(s) \
{ \
.name = #s, \
.addr = (unsigned long)s, \
}
#define ITEM_DATA(s) \
{ \
.name = #s, \
.addr = (unsigned long)&s, \
}
static int kallsyms_test_var_bss_static;
static int kallsyms_test_var_data_static = 1;
int kallsyms_test_var_bss;
int kallsyms_test_var_data = 1;
static int kallsyms_test_func_static(void)
{
kallsyms_test_var_bss_static++;
kallsyms_test_var_data_static++;
return 0;
}
int kallsyms_test_func(void)
{
return kallsyms_test_func_static();
}
__weak int kallsyms_test_func_weak(void)
{
kallsyms_test_var_bss++;
kallsyms_test_var_data++;
return 0;
}
static struct test_item test_items[] = {
ITEM_FUNC(kallsyms_test_func_static),
ITEM_FUNC(kallsyms_test_func),
ITEM_FUNC(kallsyms_test_func_weak),
ITEM_FUNC(vmalloc),
ITEM_FUNC(vfree),
#ifdef CONFIG_KALLSYMS_ALL
ITEM_DATA(kallsyms_test_var_bss_static),
ITEM_DATA(kallsyms_test_var_data_static),
ITEM_DATA(kallsyms_test_var_bss),
ITEM_DATA(kallsyms_test_var_data),
ITEM_DATA(vmap_area_list),
#endif
};
static char stub_name[KSYM_NAME_LEN];
static int stat_symbol_len(void *data, const char *name, struct module *mod, unsigned long addr)
{
*(u32 *)data += strlen(name);
return 0;
}
static void test_kallsyms_compression_ratio(void)
{
u32 pos, off, len, num;
u32 ratio, total_size, total_len = 0;
kallsyms_on_each_symbol(stat_symbol_len, &total_len);
/*
* A symbol name cannot start with a number. This stub name helps us
* traverse the entire symbol table without finding a match. It's used
* for subsequent performance tests, and its length is the average
* length of all symbol names.
*/
memset(stub_name, '4', sizeof(stub_name));
pos = total_len / kallsyms_num_syms;
stub_name[pos] = 0;
pos = 0;
num = 0;
off = 0;
while (pos < kallsyms_num_syms) {
len = kallsyms_names[off];
num++;
off++;
pos++;
if ((len & 0x80) != 0) {
len = (len & 0x7f) | (kallsyms_names[off] << 7);
num++;
off++;
}
off += len;
}
/*
* 1. The length fields is not counted
* 2. The memory occupied by array kallsyms_token_table[] and
* kallsyms_token_index[] needs to be counted.
*/
total_size = off - num;
pos = kallsyms_token_index[0xff];
total_size += pos + strlen(&kallsyms_token_table[pos]) + 1;
total_size += 0x100 * sizeof(u16);
pr_info(" ---------------------------------------------------------\n");
pr_info("| nr_symbols | compressed size | original size | ratio(%%) |\n");
pr_info("|---------------------------------------------------------|\n");
ratio = (u32)div_u64(10000ULL * total_size, total_len);
pr_info("| %10d | %10d | %10d | %2d.%-2d |\n",
kallsyms_num_syms, total_size, total_len, ratio / 100, ratio % 100);
pr_info(" ---------------------------------------------------------\n");
}
static int lookup_name(void *data, const char *name, struct module *mod, unsigned long addr)
{
u64 t0, t1, t;
unsigned long flags;
struct test_stat *stat = (struct test_stat *)data;
local_irq_save(flags);
t0 = sched_clock();
(void)kallsyms_lookup_name(name);
t1 = sched_clock();
local_irq_restore(flags);
t = t1 - t0;
if (t < stat->min)
stat->min = t;
if (t > stat->max)
stat->max = t;
stat->real_cnt++;
stat->sum += t;
return 0;
}
static void test_perf_kallsyms_lookup_name(void)
{
struct test_stat stat;
memset(&stat, 0, sizeof(stat));
stat.min = INT_MAX;
kallsyms_on_each_symbol(lookup_name, &stat);
pr_info("kallsyms_lookup_name() looked up %d symbols\n", stat.real_cnt);
pr_info("The time spent on each symbol is (ns): min=%d, max=%d, avg=%lld\n",
stat.min, stat.max, div_u64(stat.sum, stat.real_cnt));
}
static bool match_cleanup_name(const char *s, const char *name)
{
char *p;
int len;
if (!IS_ENABLED(CONFIG_LTO_CLANG))
return false;
p = strchr(s, '.');
if (!p)
return false;
len = strlen(name);
if (p - s != len)
return false;
return !strncmp(s, name, len);
}
static int find_symbol(void *data, const char *name, struct module *mod, unsigned long addr)
{
struct test_stat *stat = (struct test_stat *)data;
if (strcmp(name, stat->name) == 0 ||
(!stat->perf && match_cleanup_name(name, stat->name))) {
stat->real_cnt++;
stat->addr = addr;
if (stat->save_cnt < MAX_NUM_OF_RECORDS) {
stat->addrs[stat->save_cnt] = addr;
stat->save_cnt++;
}
if (stat->real_cnt == stat->max)
return 1;
}
return 0;
}
static void test_perf_kallsyms_on_each_symbol(void)
{
u64 t0, t1;
unsigned long flags;
struct test_stat stat;
memset(&stat, 0, sizeof(stat));
stat.max = INT_MAX;
stat.name = stub_name;
stat.perf = 1;
local_irq_save(flags);
t0 = sched_clock();
kallsyms_on_each_symbol(find_symbol, &stat);
t1 = sched_clock();
local_irq_restore(flags);
pr_info("kallsyms_on_each_symbol() traverse all: %lld ns\n", t1 - t0);
}
static int match_symbol(void *data, unsigned long addr)
{
struct test_stat *stat = (struct test_stat *)data;
stat->real_cnt++;
stat->addr = addr;
if (stat->save_cnt < MAX_NUM_OF_RECORDS) {
stat->addrs[stat->save_cnt] = addr;
stat->save_cnt++;
}
if (stat->real_cnt == stat->max)
return 1;
return 0;
}
static void test_perf_kallsyms_on_each_match_symbol(void)
{
u64 t0, t1;
unsigned long flags;
struct test_stat stat;
memset(&stat, 0, sizeof(stat));
stat.max = INT_MAX;
stat.name = stub_name;
local_irq_save(flags);
t0 = sched_clock();
kallsyms_on_each_match_symbol(match_symbol, stat.name, &stat);
t1 = sched_clock();
local_irq_restore(flags);
pr_info("kallsyms_on_each_match_symbol() traverse all: %lld ns\n", t1 - t0);
}
static int test_kallsyms_basic_function(void)
{
int i, j, ret;
int next = 0, nr_failed = 0;
char *prefix;
unsigned short rand;
unsigned long addr, lookup_addr;
char namebuf[KSYM_NAME_LEN];
struct test_stat *stat, *stat2;
stat = kmalloc(sizeof(*stat) * 2, GFP_KERNEL);
if (!stat)
return -ENOMEM;
stat2 = stat + 1;
prefix = "kallsyms_lookup_name() for";
for (i = 0; i < ARRAY_SIZE(test_items); i++) {
addr = kallsyms_lookup_name(test_items[i].name);
if (addr != test_items[i].addr) {
nr_failed++;
pr_info("%s %s failed: addr=%lx, expect %lx\n",
prefix, test_items[i].name, addr, test_items[i].addr);
}
}
prefix = "kallsyms_on_each_symbol() for";
for (i = 0; i < ARRAY_SIZE(test_items); i++) {
memset(stat, 0, sizeof(*stat));
stat->max = INT_MAX;
stat->name = test_items[i].name;
kallsyms_on_each_symbol(find_symbol, stat);
if (stat->addr != test_items[i].addr || stat->real_cnt != 1) {
nr_failed++;
pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n",
prefix, test_items[i].name,
stat->real_cnt, stat->addr, test_items[i].addr);
}
}
prefix = "kallsyms_on_each_match_symbol() for";
for (i = 0; i < ARRAY_SIZE(test_items); i++) {
memset(stat, 0, sizeof(*stat));
stat->max = INT_MAX;
stat->name = test_items[i].name;
kallsyms_on_each_match_symbol(match_symbol, test_items[i].name, stat);
if (stat->addr != test_items[i].addr || stat->real_cnt != 1) {
nr_failed++;
pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n",
prefix, test_items[i].name,
stat->real_cnt, stat->addr, test_items[i].addr);
}
}
if (nr_failed) {
kfree(stat);
return -ESRCH;
}
for (i = 0; i < kallsyms_num_syms; i++) {
addr = kallsyms_sym_address(i);
if (!is_ksym_addr(addr))
continue;
ret = lookup_symbol_name(addr, namebuf);
if (unlikely(ret)) {
namebuf[0] = 0;
goto failed;
}
/*
* The first '.' may be the initial letter, in which case the
* entire symbol name will be truncated to an empty string in
* cleanup_symbol_name(). Do not test these symbols.
*
* For example:
* cat /proc/kallsyms | awk '{print $3}' | grep -E "^\." | head
* .E_read_words
* .E_leading_bytes
* .E_trailing_bytes
* .E_write_words
* .E_copy
* .str.292.llvm.12122243386960820698
* .str.24.llvm.12122243386960820698
* .str.29.llvm.12122243386960820698
* .str.75.llvm.12122243386960820698
* .str.99.llvm.12122243386960820698
*/
if (IS_ENABLED(CONFIG_LTO_CLANG) && !namebuf[0])
continue;
lookup_addr = kallsyms_lookup_name(namebuf);
memset(stat, 0, sizeof(*stat));
stat->max = INT_MAX;
kallsyms_on_each_match_symbol(match_symbol, namebuf, stat);
/*
* kallsyms_on_each_symbol() is too slow, randomly select some
* symbols for test.
*/
if (i >= next) {
memset(stat2, 0, sizeof(*stat2));
stat2->max = INT_MAX;
stat2->name = namebuf;
kallsyms_on_each_symbol(find_symbol, stat2);
/*
* kallsyms_on_each_symbol() and kallsyms_on_each_match_symbol()
* need to get the same traversal result.
*/
if (stat->addr != stat2->addr ||
stat->real_cnt != stat2->real_cnt ||
memcmp(stat->addrs, stat2->addrs,
stat->save_cnt * sizeof(stat->addrs[0])))
goto failed;
/*
* The average of random increments is 128, that is, one of
* them is tested every 128 symbols.
*/
get_random_bytes(&rand, sizeof(rand));
next = i + (rand & 0xff) + 1;
}
/* Need to be found at least once */
if (!stat->real_cnt)
goto failed;
/*
* kallsyms_lookup_name() returns the address of the first
* symbol found and cannot be NULL.
*/
if (!lookup_addr || lookup_addr != stat->addrs[0])
goto failed;
/*
* If the addresses of all matching symbols are recorded, the
* target address needs to be exist.
*/
if (stat->real_cnt <= MAX_NUM_OF_RECORDS) {
for (j = 0; j < stat->save_cnt; j++) {
if (stat->addrs[j] == addr)
break;
}
if (j == stat->save_cnt)
goto failed;
}
}
kfree(stat);
return 0;
failed:
pr_info("Test for %dth symbol failed: (%s) addr=%lx", i, namebuf, addr);
kfree(stat);
return -ESRCH;
}
static int test_entry(void *p)
{
int ret;
do {
schedule_timeout(5 * HZ);
} while (system_state != SYSTEM_RUNNING);
pr_info("start\n");
ret = test_kallsyms_basic_function();
if (ret) {
pr_info("abort\n");
return 0;
}
test_kallsyms_compression_ratio();
test_perf_kallsyms_lookup_name();
test_perf_kallsyms_on_each_symbol();
test_perf_kallsyms_on_each_match_symbol();
pr_info("finish\n");
return 0;
}
static int __init kallsyms_test_init(void)
{
struct task_struct *t;
t = kthread_create(test_entry, NULL, "kallsyms_test");
if (IS_ERR(t)) {
pr_info("Create kallsyms selftest task failed\n");
return PTR_ERR(t);
}
kthread_bind(t, 0);
wake_up_process(t);
return 0;
}
late_initcall(kallsyms_test_init);
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef LINUX_KALLSYMS_SELFTEST_H_
#define LINUX_KALLSYMS_SELFTEST_H_
#include <linux/types.h>
extern int kallsyms_test_var_bss;
extern int kallsyms_test_var_data;
extern int kallsyms_test_func(void);
extern int kallsyms_test_func_weak(void);
#endif // LINUX_KALLSYMS_SELFTEST_H_
...@@ -125,20 +125,10 @@ struct klp_find_arg { ...@@ -125,20 +125,10 @@ struct klp_find_arg {
unsigned long pos; unsigned long pos;
}; };
static int klp_find_callback(void *data, const char *name, static int klp_match_callback(void *data, unsigned long addr)
struct module *mod, unsigned long addr)
{ {
struct klp_find_arg *args = data; struct klp_find_arg *args = data;
if ((mod && !args->objname) || (!mod && args->objname))
return 0;
if (strcmp(args->name, name))
return 0;
if (args->objname && strcmp(args->objname, mod->name))
return 0;
args->addr = addr; args->addr = addr;
args->count++; args->count++;
...@@ -153,6 +143,23 @@ static int klp_find_callback(void *data, const char *name, ...@@ -153,6 +143,23 @@ static int klp_find_callback(void *data, const char *name,
return 0; return 0;
} }
static int klp_find_callback(void *data, const char *name,
struct module *mod, unsigned long addr)
{
struct klp_find_arg *args = data;
if ((mod && !args->objname) || (!mod && args->objname))
return 0;
if (strcmp(args->name, name))
return 0;
if (args->objname && strcmp(args->objname, mod->name))
return 0;
return klp_match_callback(data, addr);
}
static int klp_find_object_symbol(const char *objname, const char *name, static int klp_find_object_symbol(const char *objname, const char *name,
unsigned long sympos, unsigned long *addr) unsigned long sympos, unsigned long *addr)
{ {
...@@ -167,7 +174,7 @@ static int klp_find_object_symbol(const char *objname, const char *name, ...@@ -167,7 +174,7 @@ static int klp_find_object_symbol(const char *objname, const char *name,
if (objname) if (objname)
module_kallsyms_on_each_symbol(klp_find_callback, &args); module_kallsyms_on_each_symbol(klp_find_callback, &args);
else else
kallsyms_on_each_symbol(klp_find_callback, &args); kallsyms_on_each_match_symbol(klp_match_callback, name, &args);
/* /*
* Ensure an address was found. If sympos is 0, ensure symbol is unique; * Ensure an address was found. If sympos is 0, ensure symbol is unique;
......
...@@ -221,9 +221,10 @@ endchoice ...@@ -221,9 +221,10 @@ endchoice
config MODULE_DECOMPRESS config MODULE_DECOMPRESS
bool "Support in-kernel module decompression" bool "Support in-kernel module decompression"
depends on MODULE_COMPRESS_GZIP || MODULE_COMPRESS_XZ depends on MODULE_COMPRESS_GZIP || MODULE_COMPRESS_XZ || MODULE_COMPRESS_ZSTD
select ZLIB_INFLATE if MODULE_COMPRESS_GZIP select ZLIB_INFLATE if MODULE_COMPRESS_GZIP
select XZ_DEC if MODULE_COMPRESS_XZ select XZ_DEC if MODULE_COMPRESS_XZ
select ZSTD_DECOMPRESS if MODULE_COMPRESS_ZSTD
help help
Support for decompressing kernel modules by the kernel itself Support for decompressing kernel modules by the kernel itself
......
...@@ -50,7 +50,7 @@ static struct page *module_get_next_page(struct load_info *info) ...@@ -50,7 +50,7 @@ static struct page *module_get_next_page(struct load_info *info)
return page; return page;
} }
#ifdef CONFIG_MODULE_COMPRESS_GZIP #if defined(CONFIG_MODULE_COMPRESS_GZIP)
#include <linux/zlib.h> #include <linux/zlib.h>
#define MODULE_COMPRESSION gzip #define MODULE_COMPRESSION gzip
#define MODULE_DECOMPRESS_FN module_gzip_decompress #define MODULE_DECOMPRESS_FN module_gzip_decompress
...@@ -114,8 +114,8 @@ static ssize_t module_gzip_decompress(struct load_info *info, ...@@ -114,8 +114,8 @@ static ssize_t module_gzip_decompress(struct load_info *info,
do { do {
struct page *page = module_get_next_page(info); struct page *page = module_get_next_page(info);
if (!page) { if (IS_ERR(page)) {
retval = -ENOMEM; retval = PTR_ERR(page);
goto out_inflate_end; goto out_inflate_end;
} }
...@@ -141,7 +141,7 @@ static ssize_t module_gzip_decompress(struct load_info *info, ...@@ -141,7 +141,7 @@ static ssize_t module_gzip_decompress(struct load_info *info,
kfree(s.workspace); kfree(s.workspace);
return retval; return retval;
} }
#elif CONFIG_MODULE_COMPRESS_XZ #elif defined(CONFIG_MODULE_COMPRESS_XZ)
#include <linux/xz.h> #include <linux/xz.h>
#define MODULE_COMPRESSION xz #define MODULE_COMPRESSION xz
#define MODULE_DECOMPRESS_FN module_xz_decompress #define MODULE_DECOMPRESS_FN module_xz_decompress
...@@ -173,8 +173,8 @@ static ssize_t module_xz_decompress(struct load_info *info, ...@@ -173,8 +173,8 @@ static ssize_t module_xz_decompress(struct load_info *info,
do { do {
struct page *page = module_get_next_page(info); struct page *page = module_get_next_page(info);
if (!page) { if (IS_ERR(page)) {
retval = -ENOMEM; retval = PTR_ERR(page);
goto out; goto out;
} }
...@@ -199,6 +199,94 @@ static ssize_t module_xz_decompress(struct load_info *info, ...@@ -199,6 +199,94 @@ static ssize_t module_xz_decompress(struct load_info *info,
xz_dec_end(xz_dec); xz_dec_end(xz_dec);
return retval; return retval;
} }
#elif defined(CONFIG_MODULE_COMPRESS_ZSTD)
#include <linux/zstd.h>
#define MODULE_COMPRESSION zstd
#define MODULE_DECOMPRESS_FN module_zstd_decompress
static ssize_t module_zstd_decompress(struct load_info *info,
const void *buf, size_t size)
{
static const u8 signature[] = { 0x28, 0xb5, 0x2f, 0xfd };
ZSTD_outBuffer zstd_dec;
ZSTD_inBuffer zstd_buf;
zstd_frame_header header;
size_t wksp_size;
void *wksp = NULL;
ZSTD_DStream *dstream;
size_t ret;
size_t new_size = 0;
int retval;
if (size < sizeof(signature) ||
memcmp(buf, signature, sizeof(signature))) {
pr_err("not a zstd compressed module\n");
return -EINVAL;
}
zstd_buf.src = buf;
zstd_buf.pos = 0;
zstd_buf.size = size;
ret = zstd_get_frame_header(&header, zstd_buf.src, zstd_buf.size);
if (ret != 0) {
pr_err("ZSTD-compressed data has an incomplete frame header\n");
retval = -EINVAL;
goto out;
}
if (header.windowSize > (1 << ZSTD_WINDOWLOG_MAX)) {
pr_err("ZSTD-compressed data has too large a window size\n");
retval = -EINVAL;
goto out;
}
wksp_size = zstd_dstream_workspace_bound(header.windowSize);
wksp = kmalloc(wksp_size, GFP_KERNEL);
if (!wksp) {
retval = -ENOMEM;
goto out;
}
dstream = zstd_init_dstream(header.windowSize, wksp, wksp_size);
if (!dstream) {
pr_err("Can't initialize ZSTD stream\n");
retval = -ENOMEM;
goto out;
}
do {
struct page *page = module_get_next_page(info);
if (!IS_ERR(page)) {
retval = PTR_ERR(page);
goto out;
}
zstd_dec.dst = kmap_local_page(page);
zstd_dec.pos = 0;
zstd_dec.size = PAGE_SIZE;
ret = zstd_decompress_stream(dstream, &zstd_dec, &zstd_buf);
kunmap(page);
retval = zstd_get_error_code(ret);
if (retval)
break;
new_size += zstd_dec.pos;
} while (zstd_dec.pos == PAGE_SIZE && ret != 0);
if (retval) {
pr_err("ZSTD-decompression failed with status %d\n", retval);
retval = -EINVAL;
goto out;
}
retval = new_size;
out:
kfree(wksp);
return retval;
}
#else #else
#error "Unexpected configuration for CONFIG_MODULE_DECOMPRESS" #error "Unexpected configuration for CONFIG_MODULE_DECOMPRESS"
#endif #endif
......
...@@ -85,9 +85,6 @@ struct mod_tree_root mod_data_tree __cacheline_aligned = { ...@@ -85,9 +85,6 @@ struct mod_tree_root mod_data_tree __cacheline_aligned = {
}; };
#endif #endif
#define module_addr_min mod_tree.addr_min
#define module_addr_max mod_tree.addr_max
struct symsearch { struct symsearch {
const struct kernel_symbol *start, *stop; const struct kernel_symbol *start, *stop;
const s32 *crcs; const s32 *crcs;
......
...@@ -340,7 +340,7 @@ static int mod_sysfs_init(struct module *mod) ...@@ -340,7 +340,7 @@ static int mod_sysfs_init(struct module *mod)
int err; int err;
struct kobject *kobj; struct kobject *kobj;
if (!module_sysfs_initialized) { if (!module_kset) {
pr_err("%s: module sysfs not initialized\n", mod->name); pr_err("%s: module sysfs not initialized\n", mod->name);
err = -EINVAL; err = -EINVAL;
goto out; goto out;
......
...@@ -940,7 +940,6 @@ static const struct kset_uevent_ops module_uevent_ops = { ...@@ -940,7 +940,6 @@ static const struct kset_uevent_ops module_uevent_ops = {
}; };
struct kset *module_kset; struct kset *module_kset;
int module_sysfs_initialized;
static void module_kobj_release(struct kobject *kobj) static void module_kobj_release(struct kobject *kobj)
{ {
...@@ -954,7 +953,11 @@ struct kobj_type module_ktype = { ...@@ -954,7 +953,11 @@ struct kobj_type module_ktype = {
}; };
/* /*
* param_sysfs_init - wrapper for built-in params support * param_sysfs_init - create "module" kset
*
* This must be done before the initramfs is unpacked and
* request_module() thus becomes possible, because otherwise the
* module load would fail in mod_sysfs_init.
*/ */
static int __init param_sysfs_init(void) static int __init param_sysfs_init(void)
{ {
...@@ -964,13 +967,25 @@ static int __init param_sysfs_init(void) ...@@ -964,13 +967,25 @@ static int __init param_sysfs_init(void)
__FILE__, __LINE__); __FILE__, __LINE__);
return -ENOMEM; return -ENOMEM;
} }
module_sysfs_initialized = 1;
return 0;
}
subsys_initcall(param_sysfs_init);
/*
* param_sysfs_builtin_init - add sysfs version and parameter
* attributes for built-in modules
*/
static int __init param_sysfs_builtin_init(void)
{
if (!module_kset)
return -ENOMEM;
version_sysfs_builtin(); version_sysfs_builtin();
param_sysfs_builtin(); param_sysfs_builtin();
return 0; return 0;
} }
subsys_initcall(param_sysfs_init); late_initcall(param_sysfs_builtin_init);
#endif /* CONFIG_SYSFS */ #endif /* CONFIG_SYSFS */
...@@ -49,6 +49,7 @@ _Static_assert( ...@@ -49,6 +49,7 @@ _Static_assert(
struct sym_entry { struct sym_entry {
unsigned long long addr; unsigned long long addr;
unsigned int len; unsigned int len;
unsigned int seq;
unsigned int start_pos; unsigned int start_pos;
unsigned int percpu_absolute; unsigned int percpu_absolute;
unsigned char sym[]; unsigned char sym[];
...@@ -77,6 +78,7 @@ static unsigned int table_size, table_cnt; ...@@ -77,6 +78,7 @@ static unsigned int table_size, table_cnt;
static int all_symbols; static int all_symbols;
static int absolute_percpu; static int absolute_percpu;
static int base_relative; static int base_relative;
static int lto_clang;
static int token_profit[0x10000]; static int token_profit[0x10000];
...@@ -88,7 +90,7 @@ static unsigned char best_table_len[256]; ...@@ -88,7 +90,7 @@ static unsigned char best_table_len[256];
static void usage(void) static void usage(void)
{ {
fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] " fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] "
"[--base-relative] in.map > out.S\n"); "[--base-relative] [--lto-clang] in.map > out.S\n");
exit(1); exit(1);
} }
...@@ -410,6 +412,65 @@ static int symbol_absolute(const struct sym_entry *s) ...@@ -410,6 +412,65 @@ static int symbol_absolute(const struct sym_entry *s)
return s->percpu_absolute; return s->percpu_absolute;
} }
static char * s_name(char *buf)
{
/* Skip the symbol type */
return buf + 1;
}
static void cleanup_symbol_name(char *s)
{
char *p;
if (!lto_clang)
return;
/*
* ASCII[.] = 2e
* ASCII[0-9] = 30,39
* ASCII[A-Z] = 41,5a
* ASCII[_] = 5f
* ASCII[a-z] = 61,7a
*
* As above, replacing '.' with '\0' does not affect the main sorting,
* but it helps us with subsorting.
*/
p = strchr(s, '.');
if (p)
*p = '\0';
}
static int compare_names(const void *a, const void *b)
{
int ret;
char sa_namebuf[KSYM_NAME_LEN];
char sb_namebuf[KSYM_NAME_LEN];
const struct sym_entry *sa = *(const struct sym_entry **)a;
const struct sym_entry *sb = *(const struct sym_entry **)b;
expand_symbol(sa->sym, sa->len, sa_namebuf);
expand_symbol(sb->sym, sb->len, sb_namebuf);
cleanup_symbol_name(s_name(sa_namebuf));
cleanup_symbol_name(s_name(sb_namebuf));
ret = strcmp(s_name(sa_namebuf), s_name(sb_namebuf));
if (!ret) {
if (sa->addr > sb->addr)
return 1;
else if (sa->addr < sb->addr)
return -1;
/* keep old order */
return (int)(sa->seq - sb->seq);
}
return ret;
}
static void sort_symbols_by_name(void)
{
qsort(table, table_cnt, sizeof(table[0]), compare_names);
}
static void write_src(void) static void write_src(void)
{ {
unsigned int i, k, off; unsigned int i, k, off;
...@@ -495,6 +556,7 @@ static void write_src(void) ...@@ -495,6 +556,7 @@ static void write_src(void)
for (i = 0; i < table_cnt; i++) { for (i = 0; i < table_cnt; i++) {
if ((i & 0xFF) == 0) if ((i & 0xFF) == 0)
markers[i >> 8] = off; markers[i >> 8] = off;
table[i]->seq = i;
/* There cannot be any symbol of length zero. */ /* There cannot be any symbol of length zero. */
if (table[i]->len == 0) { if (table[i]->len == 0) {
...@@ -535,6 +597,15 @@ static void write_src(void) ...@@ -535,6 +597,15 @@ static void write_src(void)
free(markers); free(markers);
sort_symbols_by_name();
output_label("kallsyms_seqs_of_names");
for (i = 0; i < table_cnt; i++)
printf("\t.byte 0x%02x, 0x%02x, 0x%02x\n",
(unsigned char)(table[i]->seq >> 16),
(unsigned char)(table[i]->seq >> 8),
(unsigned char)(table[i]->seq >> 0));
printf("\n");
output_label("kallsyms_token_table"); output_label("kallsyms_token_table");
off = 0; off = 0;
for (i = 0; i < 256; i++) { for (i = 0; i < 256; i++) {
...@@ -573,7 +644,7 @@ static void forget_symbol(const unsigned char *symbol, int len) ...@@ -573,7 +644,7 @@ static void forget_symbol(const unsigned char *symbol, int len)
} }
/* do the initial token count */ /* do the initial token count */
static void build_initial_tok_table(void) static void build_initial_token_table(void)
{ {
unsigned int i; unsigned int i;
...@@ -698,7 +769,7 @@ static void insert_real_symbols_in_table(void) ...@@ -698,7 +769,7 @@ static void insert_real_symbols_in_table(void)
static void optimize_token_table(void) static void optimize_token_table(void)
{ {
build_initial_tok_table(); build_initial_token_table();
insert_real_symbols_in_table(); insert_real_symbols_in_table();
...@@ -818,6 +889,7 @@ int main(int argc, char **argv) ...@@ -818,6 +889,7 @@ int main(int argc, char **argv)
{"all-symbols", no_argument, &all_symbols, 1}, {"all-symbols", no_argument, &all_symbols, 1},
{"absolute-percpu", no_argument, &absolute_percpu, 1}, {"absolute-percpu", no_argument, &absolute_percpu, 1},
{"base-relative", no_argument, &base_relative, 1}, {"base-relative", no_argument, &base_relative, 1},
{"lto-clang", no_argument, &lto_clang, 1},
{}, {},
}; };
......
...@@ -156,6 +156,10 @@ kallsyms() ...@@ -156,6 +156,10 @@ kallsyms()
kallsymopt="${kallsymopt} --base-relative" kallsymopt="${kallsymopt} --base-relative"
fi fi
if is_enabled CONFIG_LTO_CLANG; then
kallsymopt="${kallsymopt} --lto-clang"
fi
info KSYMS ${2} info KSYMS ${2}
scripts/kallsyms ${kallsymopt} ${1} > ${2} scripts/kallsyms ${kallsymopt} ${1} > ${2}
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment