From: Sasha Levin <sashal@kernel.org>
To: Andrew Morton <akpm@linux-foundation.org>,
Masahiro Yamada <masahiroy@kernel.org>,
Nathan Chancellor <nathan@kernel.org>,
Nicolas Schier <nsc@kernel.org>
Cc: Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
"H. Peter Anvin" <hpa@zytor.com>,
Peter Zijlstra <peterz@infradead.org>,
Josh Poimboeuf <jpoimboe@kernel.org>,
Petr Mladek <pmladek@suse.com>,
Alexei Starovoitov <ast@kernel.org>,
Jonathan Corbet <corbet@lwn.net>, David Gow <davidgow@google.com>,
Kees Cook <kees@kernel.org>, Greg KH <gregkh@linuxfoundation.org>,
Luis Chamberlain <mcgrof@kernel.org>,
Steven Rostedt <rostedt@goodmis.org>,
Helge Deller <deller@gmx.de>,
Randy Dunlap <rdunlap@infradead.org>,
Geert Uytterhoeven <geert@linux-m68k.org>,
Juergen Gross <jgross@suse.com>,
James Bottomley <James.Bottomley@HansenPartnership.com>,
Alexey Dobriyan <adobriyan@gmail.com>,
Vlastimil Babka <vbabka@kernel.org>,
Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
Petr Pavlu <petr.pavlu@suse.com>,
x86@kernel.org, linux-kernel@vger.kernel.org,
linux-kbuild@vger.kernel.org, linux-doc@vger.kernel.org,
linux-modules@vger.kernel.org, bpf@vger.kernel.org,
Sasha Levin <sashal@kernel.org>
Subject: [PATCH 1/2] kallsyms: show function parameter info in oops/WARN dumps
Date: Mon, 23 Mar 2026 12:48:56 -0400 [thread overview]
Message-ID: <20260323164858.1939248-2-sashal@kernel.org> (raw)
In-Reply-To: <20260323164858.1939248-1-sashal@kernel.org>
Embed DWARF-derived function parameter name and type information in the
kernel image so that oops and WARN dumps display the crashing function's
register-passed arguments with their names, types, and values.
A new build-time tool (scripts/gen_paraminfo.c) parses DW_TAG_subprogram
and DW_TAG_formal_parameter entries from DWARF .debug_info, extracting
parameter names and human-readable type strings. The resulting tables are
stored in .rodata using the same two-phase link approach as lineinfo.
At runtime, kallsyms_show_paraminfo() performs a binary search on the
paraminfo tables, maps parameters to x86-64 calling convention registers
(RDI, RSI, RDX, RCX, R8, R9), and prints each parameter's name, type,
and value from pt_regs. If a parameter value matches the page fault
address (CR2), it is highlighted with "<-- fault address".
Integration at show_regs() means this works for both oops and WARN()
automatically, since both paths provide full pt_regs at the exception
point.
Example output:
Function parameters (ext4_readdir):
file (struct file *) = 0xffff888123456000
ctx (struct dir_context *) = 0x0000000000001234 <-- fault address
Gated behind CONFIG_KALLSYMS_PARAMINFO (depends on CONFIG_KALLSYMS_LINEINFO).
Adds approximately 1-2 MB to the kernel image for ~58K functions.
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
.../admin-guide/kallsyms-lineinfo.rst | 31 +
arch/x86/kernel/dumpstack.c | 6 +-
include/linux/kallsyms.h | 9 +
init/Kconfig | 21 +
kernel/kallsyms.c | 168 ++++++
kernel/kallsyms_internal.h | 6 +
lib/Kconfig.debug | 11 +
lib/tests/Makefile | 3 +
lib/tests/paraminfo_kunit.c | 256 ++++++++
scripts/Makefile | 3 +
scripts/empty_paraminfo.S | 18 +
scripts/gen_paraminfo.c | 549 ++++++++++++++++++
scripts/link-vmlinux.sh | 44 +-
13 files changed, 1119 insertions(+), 6 deletions(-)
create mode 100644 lib/tests/paraminfo_kunit.c
create mode 100644 scripts/empty_paraminfo.S
create mode 100644 scripts/gen_paraminfo.c
diff --git a/Documentation/admin-guide/kallsyms-lineinfo.rst b/Documentation/admin-guide/kallsyms-lineinfo.rst
index dd264830c8d5b..26921bb3f7f81 100644
--- a/Documentation/admin-guide/kallsyms-lineinfo.rst
+++ b/Documentation/admin-guide/kallsyms-lineinfo.rst
@@ -83,6 +83,37 @@ compression).
Per-module lineinfo adds approximately 2-3 bytes per DWARF line entry to each
``.ko`` file.
+Function Parameter Info
+======================
+
+``CONFIG_KALLSYMS_PARAMINFO`` extends the debugging information by embedding
+function parameter names and types in the kernel image. When an oops or WARN
+occurs, the faulting function's register-passed arguments are displayed with
+their names, types, and values from the saved registers.
+
+Enable in addition to the lineinfo options::
+
+ CONFIG_KALLSYMS_PARAMINFO=y
+
+Example oops output with paraminfo::
+
+ RIP: 0010:ext4_readdir+0x1a3/0x5b0 (fs/ext4/dir.c:421)
+ ...
+ Function parameters (ext4_readdir):
+ file (struct file *) = 0xffff888123456000
+ ctx (struct dir_context *) = 0x0000000000001234 <-- fault address
+
+The ``<-- fault address`` annotation appears when a parameter value matches
+the page fault address (CR2 on x86), helping quickly identify which argument
+caused the crash.
+
+This feature works for both oops and WARN() on x86-64. Only register-passed
+parameters (up to 6 on x86-64: RDI, RSI, RDX, RCX, R8, R9) are displayed.
+The parameter info is only shown for the faulting/warning frame where full
+register state is available.
+
+The paraminfo tables add approximately 1-2 MiB to the kernel image.
+
Known Limitations
=================
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index b10684dedc589..4e9b5fd58fd1b 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -483,8 +483,10 @@ void show_regs(struct pt_regs *regs)
__show_regs(regs, print_kernel_regs, KERN_DEFAULT);
/*
- * When in-kernel, we also print out the stack at the time of the fault..
+ * When in-kernel, show function parameter info and stack trace.
*/
- if (!user_mode(regs))
+ if (!user_mode(regs)) {
+ kallsyms_show_paraminfo(regs);
show_trace_log_lvl(current, regs, NULL, KERN_DEFAULT);
+ }
}
diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
index 7d4c9dca06c87..17c9df520b2b0 100644
--- a/include/linux/kallsyms.h
+++ b/include/linux/kallsyms.h
@@ -104,6 +104,13 @@ int lookup_symbol_name(unsigned long addr, char *symname);
bool kallsyms_lookup_lineinfo(unsigned long addr,
const char **file, unsigned int *line);
+#ifdef CONFIG_KALLSYMS_PARAMINFO
+struct pt_regs;
+void kallsyms_show_paraminfo(struct pt_regs *regs);
+#else
+static inline void kallsyms_show_paraminfo(struct pt_regs *regs) {}
+#endif
+
#else /* !CONFIG_KALLSYMS */
static inline unsigned long kallsyms_lookup_name(const char *name)
@@ -179,6 +186,8 @@ static inline bool kallsyms_lookup_lineinfo(unsigned long addr,
{
return false;
}
+
+static inline void kallsyms_show_paraminfo(struct pt_regs *regs) {}
#endif /*CONFIG_KALLSYMS*/
static inline void print_ip_sym(const char *loglvl, unsigned long ip)
diff --git a/init/Kconfig b/init/Kconfig
index 6e3795b3dbd62..76d0c2da7d612 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2085,6 +2085,27 @@ config KALLSYMS_LINEINFO_MODULES
If unsure, say N.
+config KALLSYMS_PARAMINFO
+ bool "Show function parameter info in oops/WARN dumps"
+ depends on KALLSYMS_LINEINFO
+ help
+ Embeds function parameter name and type information in the kernel
+ image, extracted from DWARF debug info at build time. When an
+ oops or WARN occurs, the crashing/warning function's register-
+ passed arguments are displayed with their names, types, and
+ values from pt_regs.
+
+ When enabled, oops/WARN dumps include lines like:
+
+ Function parameters (ext4_readdir):
+ file (struct file *) = 0xffff888123456000
+ ctx (struct dir_context *) = 0x0000000000001234
+
+ Requires elfutils (libdw-dev/elfutils-devel) on the build host.
+ Adds approximately 1-2 MB to the kernel image.
+
+ If unsure, say N.
+
# end of the "standard kernel features (expert users)" menu
config ARCH_HAS_MEMBARRIER_CALLBACKS
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index e6f796d43dd70..af8de3d8e3ba3 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -501,6 +501,174 @@ bool kallsyms_lookup_lineinfo(unsigned long addr,
return lineinfo_search(&tbl, (unsigned int)raw_offset, file, line);
}
+#ifdef CONFIG_KALLSYMS_PARAMINFO
+
+#include <linux/ptrace.h>
+#ifdef CONFIG_X86
+#include <asm/special_insns.h>
+#endif
+
+#define MAX_PARAMINFO_PARAMS 6
+
+/*
+ * x86-64 calling convention: arguments are passed in registers
+ * RDI, RSI, RDX, RCX, R8, R9 (in that order).
+ */
+#ifdef CONFIG_X86_64
+
+static const char * const paraminfo_reg_names[] = {
+ "RDI", "RSI", "RDX", "RCX", "R8", "R9"
+};
+
+static unsigned long paraminfo_get_reg(const struct pt_regs *regs,
+ unsigned int idx)
+{
+ switch (idx) {
+ case 0: return regs->di;
+ case 1: return regs->si;
+ case 2: return regs->dx;
+ case 3: return regs->cx;
+ case 4: return regs->r8;
+ case 5: return regs->r9;
+ default: return 0;
+ }
+}
+#else
+/* Stub for non-x86-64 architectures */
+static const char * const paraminfo_reg_names[] = {};
+
+static unsigned long paraminfo_get_reg(const struct pt_regs *regs,
+ unsigned int idx)
+{
+ return 0;
+}
+#endif /* CONFIG_X86_64 */
+
+/*
+ * Binary search for the function containing the given offset in
+ * paraminfo_func_addrs[]. Returns the index of the function whose
+ * start address is <= offset, or -1 if not found.
+ */
+static int paraminfo_find_func(unsigned int offset)
+{
+ int lo = 0, hi = paraminfo_num_funcs - 1;
+ int result = -1;
+
+ if (!paraminfo_num_funcs)
+ return -1;
+
+ while (lo <= hi) {
+ int mid = lo + (hi - lo) / 2;
+
+ if (paraminfo_func_addrs[mid] <= offset) {
+ result = mid;
+ lo = mid + 1;
+ } else {
+ hi = mid - 1;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Show function parameter info for the faulting/warning instruction.
+ *
+ * Called from show_regs() on x86 when CONFIG_KALLSYMS_PARAMINFO is
+ * enabled. Works for both oops (page fault, GPF, etc.) and WARN(),
+ * since both paths provide full pt_regs at the exception point.
+ */
+void kallsyms_show_paraminfo(struct pt_regs *regs)
+{
+ unsigned long long raw_offset;
+ unsigned int offset;
+ int func_idx;
+ const u8 *data;
+ unsigned int num_params, i;
+ unsigned long ip, fault_addr;
+ char sym_name[KSYM_NAME_LEN];
+ unsigned long sym_size, sym_offset;
+
+ if (!regs || !paraminfo_num_funcs)
+ return;
+
+ ip = regs->ip;
+
+ /* Only handle kernel-mode faults */
+ if (user_mode(regs))
+ return;
+
+ if (ip < (unsigned long)_text)
+ return;
+
+ raw_offset = ip - (unsigned long)_text;
+ if (raw_offset > UINT_MAX)
+ return;
+ offset = (unsigned int)raw_offset;
+
+ func_idx = paraminfo_find_func(offset);
+ if (func_idx < 0)
+ return;
+
+ /*
+ * Verify the IP is within a reasonable range of the function
+ * start. paraminfo_func_addrs[] contains function start offsets;
+ * check that we're not too far past the start. Use kallsyms to
+ * verify we're in the right function.
+ */
+ if (!kallsyms_lookup_size_offset(ip, &sym_size, &sym_offset))
+ return;
+
+ /* Decode the function's parameter data */
+ data = paraminfo_func_data + paraminfo_func_offsets[func_idx];
+ num_params = *data++;
+
+ if (num_params == 0 || num_params > MAX_PARAMINFO_PARAMS)
+ return;
+
+ /* Look up function name for the header */
+ if (lookup_symbol_name(ip - sym_offset, sym_name))
+ return;
+
+ /*
+ * Read the fault address for highlighting. On x86, CR2 holds
+ * the page fault linear address. On other architectures this
+ * would need a different mechanism.
+ */
+#ifdef CONFIG_X86
+ fault_addr = read_cr2();
+#else
+ fault_addr = 0;
+#endif
+
+ printk(KERN_DEFAULT "Function parameters (%s):\n", sym_name);
+
+ for (i = 0; i < num_params; i++) {
+ u32 name_off, type_off;
+ const char *pname, *ptype;
+ unsigned long val;
+ bool is_fault_addr;
+
+ memcpy(&name_off, data, sizeof(u32));
+ data += sizeof(u32);
+ memcpy(&type_off, data, sizeof(u32));
+ data += sizeof(u32);
+
+ pname = paraminfo_strings + name_off;
+ ptype = paraminfo_strings + type_off;
+ val = paraminfo_get_reg(regs, i);
+
+ is_fault_addr = fault_addr && (val == fault_addr);
+
+ printk(KERN_DEFAULT " %-8s (%-20s) = 0x%016lx%s\n",
+ pname, ptype, val,
+ is_fault_addr ? " <-- fault address" : "");
+ }
+}
+EXPORT_SYMBOL_GPL(kallsyms_show_paraminfo);
+
+#endif /* CONFIG_KALLSYMS_PARAMINFO */
+
/* Look up a kernel symbol and return it in a text buffer. */
static int __sprint_symbol(char *buffer, unsigned long address,
int symbol_offset, int add_offset, int add_buildid)
diff --git a/kernel/kallsyms_internal.h b/kernel/kallsyms_internal.h
index ffe4c658067ec..7287ee0859515 100644
--- a/kernel/kallsyms_internal.h
+++ b/kernel/kallsyms_internal.h
@@ -26,4 +26,10 @@ extern const u32 lineinfo_file_offsets[];
extern const u32 lineinfo_filenames_size;
extern const char lineinfo_filenames[];
+extern const u32 paraminfo_num_funcs;
+extern const u32 paraminfo_func_addrs[];
+extern const u32 paraminfo_func_offsets[];
+extern const u8 paraminfo_func_data[];
+extern const char paraminfo_strings[];
+
#endif // LINUX_KALLSYMS_INTERNAL_H_
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 688bbcb3eaa62..be8cee0985fbd 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3058,6 +3058,17 @@ config LINEINFO_KUNIT_TEST
If unsure, say N.
+config PARAMINFO_KUNIT_TEST
+ tristate "KUnit tests for kallsyms paraminfo" if !KUNIT_ALL_TESTS
+ depends on KUNIT && KALLSYMS_PARAMINFO
+ default KUNIT_ALL_TESTS
+ help
+ KUnit tests for the kallsyms function parameter info feature.
+ Verifies that paraminfo tables correctly map functions to their
+ parameter names and types.
+
+ If unsure, say N.
+
config HW_BREAKPOINT_KUNIT_TEST
bool "Test hw_breakpoint constraints accounting" if !KUNIT_ALL_TESTS
depends on HAVE_HW_BREAKPOINT
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index c6add3b04bbd5..70452942baf45 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -39,6 +39,9 @@ obj-$(CONFIG_LONGEST_SYM_KUNIT_TEST) += longest_symbol_kunit.o
CFLAGS_lineinfo_kunit.o += $(call cc-option,-fno-inline-functions-called-once)
obj-$(CONFIG_LINEINFO_KUNIT_TEST) += lineinfo_kunit.o
+CFLAGS_paraminfo_kunit.o += $(call cc-option,-fno-inline-functions-called-once)
+obj-$(CONFIG_PARAMINFO_KUNIT_TEST) += paraminfo_kunit.o
+
obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o
obj-$(CONFIG_MIN_HEAP_KUNIT_TEST) += min_heap_kunit.o
CFLAGS_overflow_kunit.o = $(call cc-disable-warning, tautological-constant-out-of-range-compare)
diff --git a/lib/tests/paraminfo_kunit.c b/lib/tests/paraminfo_kunit.c
new file mode 100644
index 0000000000000..e09efc4ddeb0e
--- /dev/null
+++ b/lib/tests/paraminfo_kunit.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for kallsyms paraminfo (CONFIG_KALLSYMS_PARAMINFO).
+ *
+ * Copyright (c) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * Verifies that the paraminfo tables correctly map function addresses
+ * to their parameter names and types.
+ *
+ * Build with: CONFIG_PARAMINFO_KUNIT_TEST=m (or =y)
+ */
+
+#include <kunit/test.h>
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+/* ---- Test target functions with known signatures ---- */
+
+static noinline int paraminfo_test_two_args(struct kunit *test, int value)
+{
+ /* Prevent optimization */
+ return test ? value + 1 : 0;
+}
+
+static noinline void *paraminfo_test_ptr_arg(void *ptr, unsigned long size)
+{
+ if (ptr && size > 0)
+ return ptr;
+ return NULL;
+}
+
+static noinline long paraminfo_test_many_args(int a, int b, int c,
+ int d, int e, int f)
+{
+ return (long)a + b + c + d + e + f;
+}
+
+static noinline void paraminfo_test_no_args(void)
+{
+ /* Function with no parameters */
+ barrier();
+}
+
+/* ---- Helpers to query paraminfo tables directly ---- */
+
+/*
+ * These access the raw paraminfo tables to verify correctness.
+ * The tables are defined in kernel/kallsyms_internal.h.
+ */
+extern const u32 paraminfo_num_funcs;
+extern const u32 paraminfo_func_addrs[];
+extern const u32 paraminfo_func_offsets[];
+extern const u8 paraminfo_func_data[];
+extern const char paraminfo_strings[];
+
+struct param_result {
+ unsigned int num_params;
+ const char *names[6];
+ const char *types[6];
+};
+
+/*
+ * Look up paraminfo for a given kernel address.
+ * Returns true if found, filling in @result.
+ */
+static bool lookup_paraminfo(unsigned long addr, struct param_result *result)
+{
+ unsigned long long raw_offset;
+ unsigned int offset;
+ int lo, hi, func_idx;
+ const u8 *data;
+ unsigned int i;
+
+ if (!paraminfo_num_funcs)
+ return false;
+
+ if (addr < (unsigned long)_text)
+ return false;
+
+ raw_offset = addr - (unsigned long)_text;
+ if (raw_offset > UINT_MAX)
+ return false;
+ offset = (unsigned int)raw_offset;
+
+ /* Binary search for the function */
+ lo = 0;
+ hi = paraminfo_num_funcs - 1;
+ func_idx = -1;
+ while (lo <= hi) {
+ int mid = lo + (hi - lo) / 2;
+
+ if (paraminfo_func_addrs[mid] <= offset) {
+ func_idx = mid;
+ lo = mid + 1;
+ } else {
+ hi = mid - 1;
+ }
+ }
+
+ if (func_idx < 0)
+ return false;
+
+ /* Verify we're not too far from the function start */
+ if (offset - paraminfo_func_addrs[func_idx] > 0x10000)
+ return false;
+
+ data = paraminfo_func_data + paraminfo_func_offsets[func_idx];
+ result->num_params = *data++;
+
+ if (result->num_params > 6)
+ return false;
+
+ for (i = 0; i < result->num_params; i++) {
+ u32 name_off, type_off;
+
+ memcpy(&name_off, data, sizeof(u32));
+ data += sizeof(u32);
+ memcpy(&type_off, data, sizeof(u32));
+ data += sizeof(u32);
+
+ result->names[i] = paraminfo_strings + name_off;
+ result->types[i] = paraminfo_strings + type_off;
+ }
+
+ return true;
+}
+
+/* ---- Test cases ---- */
+
+static void test_paraminfo_two_args(struct kunit *test)
+{
+ struct param_result res;
+ bool found;
+
+ found = lookup_paraminfo((unsigned long)paraminfo_test_two_args, &res);
+
+ if (!IS_ENABLED(CONFIG_KALLSYMS_PARAMINFO)) {
+ KUNIT_EXPECT_FALSE(test, found);
+ return;
+ }
+
+ KUNIT_ASSERT_TRUE(test, found);
+ KUNIT_EXPECT_EQ(test, res.num_params, 2U);
+ KUNIT_EXPECT_STREQ(test, res.names[0], "test");
+ KUNIT_EXPECT_STREQ(test, res.names[1], "value");
+ KUNIT_EXPECT_TRUE(test, strstr(res.types[1], "int") != NULL);
+}
+
+static void test_paraminfo_ptr_arg(struct kunit *test)
+{
+ struct param_result res;
+ bool found;
+
+ found = lookup_paraminfo((unsigned long)paraminfo_test_ptr_arg, &res);
+
+ if (!IS_ENABLED(CONFIG_KALLSYMS_PARAMINFO)) {
+ KUNIT_EXPECT_FALSE(test, found);
+ return;
+ }
+
+ KUNIT_ASSERT_TRUE(test, found);
+ KUNIT_EXPECT_EQ(test, res.num_params, 2U);
+ KUNIT_EXPECT_STREQ(test, res.names[0], "ptr");
+ KUNIT_EXPECT_STREQ(test, res.names[1], "size");
+ /* First param should be a pointer type */
+ KUNIT_EXPECT_TRUE(test, strstr(res.types[0], "*") != NULL);
+}
+
+static void test_paraminfo_many_args(struct kunit *test)
+{
+ struct param_result res;
+ bool found;
+
+ found = lookup_paraminfo((unsigned long)paraminfo_test_many_args, &res);
+
+ if (!IS_ENABLED(CONFIG_KALLSYMS_PARAMINFO)) {
+ KUNIT_EXPECT_FALSE(test, found);
+ return;
+ }
+
+ KUNIT_ASSERT_TRUE(test, found);
+ KUNIT_EXPECT_EQ(test, res.num_params, 6U);
+ KUNIT_EXPECT_STREQ(test, res.names[0], "a");
+ KUNIT_EXPECT_STREQ(test, res.names[1], "b");
+ KUNIT_EXPECT_STREQ(test, res.names[2], "c");
+ KUNIT_EXPECT_STREQ(test, res.names[3], "d");
+ KUNIT_EXPECT_STREQ(test, res.names[4], "e");
+ KUNIT_EXPECT_STREQ(test, res.names[5], "f");
+}
+
+static void test_paraminfo_no_args(struct kunit *test)
+{
+ struct param_result res;
+ bool found;
+
+ /*
+ * Functions with no parameters should not have entries in the
+ * paraminfo table (they are filtered out at build time).
+ * The lookup may find a nearby function instead, but if it
+ * does find our function exactly, num_params should be 0.
+ */
+ found = lookup_paraminfo((unsigned long)paraminfo_test_no_args, &res);
+
+ if (found) {
+ /* If it matched our exact function, it should have 0 params.
+ * But it may have matched a preceding function instead.
+ */
+ kunit_info(test, "lookup found func with %u params\n",
+ res.num_params);
+ }
+}
+
+static void test_paraminfo_bogus_addr(struct kunit *test)
+{
+ struct param_result res;
+
+ /* Address 0 should not match anything */
+ KUNIT_EXPECT_FALSE(test, lookup_paraminfo(0, &res));
+
+ /* Address in userspace should not match */
+ KUNIT_EXPECT_FALSE(test, lookup_paraminfo(0x1000, &res));
+}
+
+static void test_paraminfo_tables_present(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_KALLSYMS_PARAMINFO)) {
+ kunit_skip(test, "CONFIG_KALLSYMS_PARAMINFO not enabled");
+ return;
+ }
+
+ KUNIT_EXPECT_GT(test, paraminfo_num_funcs, 0U);
+ kunit_info(test, "paraminfo: %u functions in table\n",
+ paraminfo_num_funcs);
+}
+
+static struct kunit_case paraminfo_test_cases[] = {
+ KUNIT_CASE(test_paraminfo_tables_present),
+ KUNIT_CASE(test_paraminfo_two_args),
+ KUNIT_CASE(test_paraminfo_ptr_arg),
+ KUNIT_CASE(test_paraminfo_many_args),
+ KUNIT_CASE(test_paraminfo_no_args),
+ KUNIT_CASE(test_paraminfo_bogus_addr),
+ {}
+};
+
+static struct kunit_suite paraminfo_test_suite = {
+ .name = "paraminfo",
+ .test_cases = paraminfo_test_cases,
+};
+
+kunit_test_suite(paraminfo_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit tests for kallsyms paraminfo");
diff --git a/scripts/Makefile b/scripts/Makefile
index ffe89875b3295..f681b94c6d9e7 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -5,6 +5,7 @@
hostprogs-always-$(CONFIG_KALLSYMS) += kallsyms
hostprogs-always-$(CONFIG_KALLSYMS_LINEINFO) += gen_lineinfo
+hostprogs-always-$(CONFIG_KALLSYMS_PARAMINFO) += gen_paraminfo
hostprogs-always-$(BUILD_C_RECORDMCOUNT) += recordmcount
hostprogs-always-$(CONFIG_BUILDTIME_TABLE_SORT) += sorttable
hostprogs-always-$(CONFIG_ASN1) += asn1_compiler
@@ -39,6 +40,8 @@ HOSTCFLAGS_sign-file.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/nu
HOSTLDLIBS_sign-file = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
HOSTCFLAGS_gen_lineinfo.o = $(shell $(HOSTPKG_CONFIG) --cflags libdw 2> /dev/null)
HOSTLDLIBS_gen_lineinfo = $(shell $(HOSTPKG_CONFIG) --libs libdw 2> /dev/null || echo -ldw -lelf -lz)
+HOSTCFLAGS_gen_paraminfo.o = $(shell $(HOSTPKG_CONFIG) --cflags libdw 2> /dev/null)
+HOSTLDLIBS_gen_paraminfo = $(shell $(HOSTPKG_CONFIG) --libs libdw 2> /dev/null || echo -ldw -lelf -lz)
ifdef CONFIG_UNWINDER_ORC
ifeq ($(ARCH),x86_64)
diff --git a/scripts/empty_paraminfo.S b/scripts/empty_paraminfo.S
new file mode 100644
index 0000000000000..a0e39dbfbd3cb
--- /dev/null
+++ b/scripts/empty_paraminfo.S
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Empty paraminfo stub for the initial vmlinux link.
+ * The real paraminfo is generated from .tmp_vmlinux1 by gen_paraminfo.
+ */
+ .section .rodata, "a"
+ .globl paraminfo_num_funcs
+ .balign 4
+paraminfo_num_funcs:
+ .long 0
+ .globl paraminfo_func_addrs
+paraminfo_func_addrs:
+ .globl paraminfo_func_offsets
+paraminfo_func_offsets:
+ .globl paraminfo_func_data
+paraminfo_func_data:
+ .globl paraminfo_strings
+paraminfo_strings:
diff --git a/scripts/gen_paraminfo.c b/scripts/gen_paraminfo.c
new file mode 100644
index 0000000000000..ea1d23f3ddd9a
--- /dev/null
+++ b/scripts/gen_paraminfo.c
@@ -0,0 +1,549 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * gen_paraminfo.c - Generate function parameter info tables from DWARF
+ *
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * Reads DWARF .debug_info from a vmlinux ELF file and outputs an assembly
+ * file containing function parameter name/type tables that the kernel uses
+ * to annotate oops/WARN dumps with typed parameter values.
+ *
+ * Requires libdw from elfutils.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <elfutils/libdw.h>
+#include <dwarf.h>
+#include <elf.h>
+#include <gelf.h>
+#include <limits.h>
+
+/* Maximum register-passed parameters on x86-64 */
+#define MAX_PARAMS 6
+
+/* Maximum length for a type name string */
+#define MAX_TYPE_LEN 64
+
+struct param_info {
+ char name[64];
+ char type[MAX_TYPE_LEN];
+};
+
+struct func_entry {
+ unsigned int offset; /* offset from _text */
+ unsigned int num_params;
+ struct param_info params[MAX_PARAMS];
+};
+
+static struct func_entry *funcs;
+static unsigned int num_funcs;
+static unsigned int funcs_capacity;
+
+/*
+ * String table for parameter names and type names.
+ * Deduplicated via a hash table.
+ */
+struct str_entry {
+ char *str;
+ unsigned int offset; /* byte offset in concatenated output */
+};
+
+static struct str_entry *strtab;
+static unsigned int num_strings;
+static unsigned int strtab_capacity;
+static unsigned int strtab_total_size;
+
+#define STR_HASH_BITS 14
+#define STR_HASH_SIZE (1 << STR_HASH_BITS)
+
+struct str_hash_entry {
+ const char *str;
+ unsigned int idx; /* index into strtab[] */
+};
+
+static struct str_hash_entry str_hash[STR_HASH_SIZE];
+
+static unsigned int hash_str(const char *s)
+{
+ unsigned int h = 5381;
+
+ for (; *s; s++)
+ h = h * 33 + (unsigned char)*s;
+ return h & (STR_HASH_SIZE - 1);
+}
+
+static unsigned int find_or_add_string(const char *s)
+{
+ unsigned int h = hash_str(s);
+
+ while (str_hash[h].str) {
+ if (!strcmp(str_hash[h].str, s))
+ return str_hash[h].idx;
+ h = (h + 1) & (STR_HASH_SIZE - 1);
+ }
+
+ if (num_strings >= strtab_capacity) {
+ strtab_capacity = strtab_capacity ? strtab_capacity * 2 : 8192;
+ strtab = realloc(strtab, strtab_capacity * sizeof(*strtab));
+ if (!strtab) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ }
+
+ strtab[num_strings].str = strdup(s);
+ strtab[num_strings].offset = strtab_total_size;
+ strtab_total_size += strlen(s) + 1;
+
+ str_hash[h].str = strtab[num_strings].str;
+ str_hash[h].idx = num_strings;
+
+ num_strings++;
+ return num_strings - 1;
+}
+
+static void add_func(struct func_entry *f)
+{
+ if (num_funcs >= funcs_capacity) {
+ funcs_capacity = funcs_capacity ? funcs_capacity * 2 : 16384;
+ funcs = realloc(funcs, funcs_capacity * sizeof(*funcs));
+ if (!funcs) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ }
+ funcs[num_funcs++] = *f;
+}
+
+/*
+ * Build a human-readable type name string from a DWARF type DIE.
+ * Follows the type chain (pointers, const, etc.) to produce strings like:
+ * "struct file *", "const char *", "unsigned long", "void *"
+ */
+static void build_type_name(Dwarf_Die *type_die, char *buf, size_t bufsz)
+{
+ Dwarf_Die child;
+ Dwarf_Attribute attr;
+ const char *name;
+ int tag;
+
+ if (!type_die) {
+ snprintf(buf, bufsz, "void");
+ return;
+ }
+
+ tag = dwarf_tag(type_die);
+
+ switch (tag) {
+ case DW_TAG_base_type:
+ name = dwarf_diename(type_die);
+ snprintf(buf, bufsz, "%s", name ? name : "?");
+ break;
+
+ case DW_TAG_pointer_type:
+ if (dwarf_attr(type_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, &child)) {
+ build_type_name(&child, buf, bufsz);
+ if (strlen(buf) + 3 < bufsz)
+ strcat(buf, " *");
+ } else {
+ snprintf(buf, bufsz, "void *");
+ }
+ break;
+
+ case DW_TAG_const_type:
+ if (dwarf_attr(type_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, &child)) {
+ char tmp[MAX_TYPE_LEN - 10];
+
+ build_type_name(&child, tmp, sizeof(tmp));
+ snprintf(buf, bufsz, "const %s", tmp);
+ } else {
+ snprintf(buf, bufsz, "const void");
+ }
+ break;
+
+ case DW_TAG_volatile_type:
+ if (dwarf_attr(type_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, &child)) {
+ char tmp[MAX_TYPE_LEN - 10];
+
+ build_type_name(&child, tmp, sizeof(tmp));
+ snprintf(buf, bufsz, "volatile %s", tmp);
+ } else {
+ snprintf(buf, bufsz, "volatile void");
+ }
+ break;
+
+ case DW_TAG_restrict_type:
+ if (dwarf_attr(type_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, &child)) {
+ build_type_name(&child, buf, bufsz);
+ } else {
+ snprintf(buf, bufsz, "void");
+ }
+ break;
+
+ case DW_TAG_typedef:
+ name = dwarf_diename(type_die);
+ if (name) {
+ snprintf(buf, bufsz, "%s", name);
+ } else if (dwarf_attr(type_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, &child)) {
+ build_type_name(&child, buf, bufsz);
+ } else {
+ snprintf(buf, bufsz, "?");
+ }
+ break;
+
+ case DW_TAG_structure_type:
+ name = dwarf_diename(type_die);
+ snprintf(buf, bufsz, "struct %s", name ? name : "(anon)");
+ break;
+
+ case DW_TAG_union_type:
+ name = dwarf_diename(type_die);
+ snprintf(buf, bufsz, "union %s", name ? name : "(anon)");
+ break;
+
+ case DW_TAG_enumeration_type:
+ name = dwarf_diename(type_die);
+ snprintf(buf, bufsz, "enum %s", name ? name : "(anon)");
+ break;
+
+ case DW_TAG_array_type:
+ if (dwarf_attr(type_die, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, &child)) {
+ build_type_name(&child, buf, bufsz);
+ if (strlen(buf) + 3 < bufsz)
+ strcat(buf, "[]");
+ } else {
+ snprintf(buf, bufsz, "?[]");
+ }
+ break;
+
+ case DW_TAG_subroutine_type:
+ snprintf(buf, bufsz, "func_ptr");
+ break;
+
+ default:
+ snprintf(buf, bufsz, "?");
+ break;
+ }
+}
+
+static unsigned long long find_text_addr(Elf *elf)
+{
+ size_t nsyms, i;
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr;
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ Elf_Data *data;
+
+ if (!gelf_getshdr(scn, &shdr))
+ continue;
+ if (shdr.sh_type != SHT_SYMTAB)
+ continue;
+
+ data = elf_getdata(scn, NULL);
+ if (!data)
+ continue;
+
+ nsyms = shdr.sh_size / shdr.sh_entsize;
+ for (i = 0; i < nsyms; i++) {
+ GElf_Sym sym;
+ const char *name;
+
+ if (!gelf_getsym(data, i, &sym))
+ continue;
+ name = elf_strptr(elf, shdr.sh_link, sym.st_name);
+ if (name && !strcmp(name, "_text"))
+ return sym.st_value;
+ }
+ }
+
+ fprintf(stderr, "Cannot find _text symbol\n");
+ exit(1);
+}
+
+static int compare_funcs(const void *a, const void *b)
+{
+ const struct func_entry *fa = a;
+ const struct func_entry *fb = b;
+
+ if (fa->offset < fb->offset)
+ return -1;
+ if (fa->offset > fb->offset)
+ return 1;
+ return 0;
+}
+
+static void process_dwarf(Dwarf *dwarf, unsigned long long text_addr)
+{
+ Dwarf_Off off = 0, next_off;
+ size_t hdr_size;
+
+ while (dwarf_nextcu(dwarf, off, &next_off, &hdr_size,
+ NULL, NULL, NULL) == 0) {
+ Dwarf_Die cudie, child;
+
+ if (!dwarf_offdie(dwarf, off + hdr_size, &cudie))
+ goto next;
+
+ if (dwarf_child(&cudie, &child) != 0)
+ goto next;
+
+ do {
+ Dwarf_Die param;
+ Dwarf_Attribute attr;
+ Dwarf_Addr low_pc;
+ struct func_entry func;
+ int tag;
+
+ tag = dwarf_tag(&child);
+ if (tag != DW_TAG_subprogram)
+ continue;
+
+ /* Skip declarations (no body) */
+ if (dwarf_attr(&child, DW_AT_declaration, &attr))
+ continue;
+
+ /* Get function start address */
+ if (dwarf_lowpc(&child, &low_pc) != 0)
+ continue;
+
+ if (low_pc < text_addr)
+ continue;
+
+ {
+ unsigned long long raw_offset = low_pc - text_addr;
+
+ if (raw_offset > UINT_MAX)
+ continue;
+ func.offset = (unsigned int)raw_offset;
+ }
+
+ /* Iterate formal parameters */
+ func.num_params = 0;
+ if (dwarf_child(&child, ¶m) == 0) {
+ do {
+ Dwarf_Die type_die;
+ const char *pname;
+
+ if (dwarf_tag(¶m) != DW_TAG_formal_parameter)
+ continue;
+ if (func.num_params >= MAX_PARAMS)
+ break;
+
+ pname = dwarf_diename(¶m);
+ if (!pname)
+ pname = "?";
+
+ snprintf(func.params[func.num_params].name,
+ sizeof(func.params[0].name),
+ "%s", pname);
+
+ /* Resolve type */
+ if (dwarf_attr(¶m, DW_AT_type, &attr) &&
+ dwarf_formref_die(&attr, &type_die)) {
+ build_type_name(&type_die,
+ func.params[func.num_params].type,
+ MAX_TYPE_LEN);
+ } else {
+ snprintf(func.params[func.num_params].type,
+ MAX_TYPE_LEN, "?");
+ }
+
+ func.num_params++;
+ } while (dwarf_siblingof(¶m, ¶m) == 0);
+ }
+
+ /* Skip functions with no parameters */
+ if (func.num_params == 0)
+ continue;
+
+ add_func(&func);
+ } while (dwarf_siblingof(&child, &child) == 0);
+next:
+ off = next_off;
+ }
+}
+
+static void deduplicate(void)
+{
+ unsigned int i, j;
+
+ if (num_funcs < 2)
+ return;
+
+ /* Sort by offset */
+ qsort(funcs, num_funcs, sizeof(*funcs), compare_funcs);
+
+ /* Remove duplicates (same offset — keep first) */
+ j = 0;
+ for (i = 1; i < num_funcs; i++) {
+ if (funcs[i].offset == funcs[j].offset)
+ continue;
+ j++;
+ if (j != i)
+ funcs[j] = funcs[i];
+ }
+ num_funcs = j + 1;
+}
+
+static void print_escaped_asciz(const char *s)
+{
+ printf("\t.asciz \"");
+ for (; *s; s++) {
+ if (*s == '"' || *s == '\\')
+ putchar('\\');
+ putchar(*s);
+ }
+ printf("\"\n");
+}
+
+static void output_assembly(void)
+{
+ unsigned int i, j;
+
+ printf("/* SPDX-License-Identifier: GPL-2.0 */\n");
+ printf("/*\n");
+ printf(" * Automatically generated by scripts/gen_paraminfo\n");
+ printf(" * Do not edit.\n");
+ printf(" */\n\n");
+
+ printf("\t.section .rodata, \"a\"\n\n");
+
+ /* Number of functions */
+ printf("\t.globl paraminfo_num_funcs\n");
+ printf("\t.balign 4\n");
+ printf("paraminfo_num_funcs:\n");
+ printf("\t.long %u\n\n", num_funcs);
+
+ /* Function address offsets (sorted, for binary search) */
+ printf("\t.globl paraminfo_func_addrs\n");
+ printf("\t.balign 4\n");
+ printf("paraminfo_func_addrs:\n");
+ for (i = 0; i < num_funcs; i++)
+ printf("\t.long 0x%x\n", funcs[i].offset);
+ printf("\n");
+
+ /*
+ * Function data offsets — byte offset into paraminfo_func_data
+ * for each function's parameter list.
+ */
+ printf("\t.globl paraminfo_func_offsets\n");
+ printf("\t.balign 4\n");
+ printf("paraminfo_func_offsets:\n");
+ for (i = 0; i < num_funcs; i++)
+ printf("\t.long .Lfunc_%u - paraminfo_func_data\n", i);
+ printf("\n");
+
+ /*
+ * Register strings in the string table and build the func data.
+ * Func data format per function:
+ * u8 num_params
+ * For each param:
+ * u32 name_str_offset
+ * u32 type_str_offset
+ */
+ /* First pass: register all strings */
+ for (i = 0; i < num_funcs; i++) {
+ for (j = 0; j < funcs[i].num_params; j++) {
+ find_or_add_string(funcs[i].params[j].name);
+ find_or_add_string(funcs[i].params[j].type);
+ }
+ }
+
+ /* Function parameter data */
+ printf("\t.globl paraminfo_func_data\n");
+ printf("paraminfo_func_data:\n");
+ for (i = 0; i < num_funcs; i++) {
+ printf(".Lfunc_%u:\n", i);
+ printf("\t.byte %u\n", funcs[i].num_params);
+ for (j = 0; j < funcs[i].num_params; j++) {
+ unsigned int name_idx = find_or_add_string(funcs[i].params[j].name);
+ unsigned int type_idx = find_or_add_string(funcs[i].params[j].type);
+
+ printf("\t.long %u, %u\n",
+ strtab[name_idx].offset,
+ strtab[type_idx].offset);
+ }
+ }
+ printf("\n");
+
+ /* String table */
+ printf("\t.globl paraminfo_strings\n");
+ printf("paraminfo_strings:\n");
+ for (i = 0; i < num_strings; i++)
+ print_escaped_asciz(strtab[i].str);
+ printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ Elf *elf;
+ Dwarf *dwarf;
+ unsigned long long text_addr;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <vmlinux ELF>\n", argv[0]);
+ return 1;
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open %s: %s\n", argv[1],
+ strerror(errno));
+ return 1;
+ }
+
+ elf_version(EV_CURRENT);
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (!elf) {
+ fprintf(stderr, "elf_begin failed: %s\n",
+ elf_errmsg(elf_errno()));
+ close(fd);
+ return 1;
+ }
+
+ text_addr = find_text_addr(elf);
+
+ dwarf = dwarf_begin_elf(elf, DWARF_C_READ, NULL);
+ if (!dwarf) {
+ fprintf(stderr, "dwarf_begin_elf failed: %s\n",
+ dwarf_errmsg(dwarf_errno()));
+ fprintf(stderr, "Is %s built with CONFIG_DEBUG_INFO?\n",
+ argv[1]);
+ elf_end(elf);
+ close(fd);
+ return 1;
+ }
+
+ process_dwarf(dwarf, text_addr);
+ deduplicate();
+
+ fprintf(stderr, "paraminfo: %u functions, %u strings\n",
+ num_funcs, num_strings);
+
+ output_assembly();
+
+ dwarf_end(dwarf);
+ elf_end(elf);
+ close(fd);
+
+ /* Cleanup */
+ free(funcs);
+ for (unsigned int i = 0; i < num_strings; i++)
+ free(strtab[i].str);
+ free(strtab);
+
+ return 0;
+}
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 39ca44fbb259b..d41b31dde4ef9 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -103,7 +103,7 @@ vmlinux_link()
${ld} ${ldflags} -o ${output} \
${wl}--whole-archive ${objs} ${wl}--no-whole-archive \
${wl}--start-group ${libs} ${wl}--end-group \
- ${kallsymso} ${lineinfo_o} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
+ ${kallsymso} ${lineinfo_o} ${paraminfo_o} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
}
# Create ${2}.o file with all symbols from the ${1} object file
@@ -149,6 +149,26 @@ gen_lineinfo()
lineinfo_o=.tmp_lineinfo.o
}
+# Generate paraminfo tables from DWARF debug info in a temporary vmlinux.
+# ${1} - temporary vmlinux with debug info
+# Output: sets paraminfo_o to the generated .o file
+gen_paraminfo()
+{
+ info PARAMINFO .tmp_paraminfo.S
+ if ! scripts/gen_paraminfo "${1}" > .tmp_paraminfo.S; then
+ echo >&2 "Failed to generate paraminfo from ${1}"
+ echo >&2 "Try to disable CONFIG_KALLSYMS_PARAMINFO"
+ exit 1
+ fi
+
+ info AS .tmp_paraminfo.o
+ ${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \
+ ${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
+ -c -o .tmp_paraminfo.o .tmp_paraminfo.S
+
+ paraminfo_o=.tmp_paraminfo.o
+}
+
# Perform kallsyms for the given temporary vmlinux.
sysmap_and_kallsyms()
{
@@ -176,6 +196,7 @@ cleanup()
{
rm -f .btf.*
rm -f .tmp_lineinfo.*
+ rm -f .tmp_paraminfo.*
rm -f .tmp_vmlinux.nm-sort
rm -f System.map
rm -f vmlinux
@@ -205,6 +226,7 @@ btf_vmlinux_bin_o=
btfids_vmlinux=
kallsymso=
lineinfo_o=
+paraminfo_o=
strip_debug=
generate_map=
@@ -229,12 +251,22 @@ if is_enabled CONFIG_KALLSYMS_LINEINFO; then
lineinfo_o=.tmp_lineinfo.o
fi
+if is_enabled CONFIG_KALLSYMS_PARAMINFO; then
+ # Assemble an empty paraminfo stub for the initial link.
+ # The real paraminfo is generated from .tmp_vmlinux1 by gen_paraminfo.
+ ${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \
+ ${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
+ -c -o .tmp_paraminfo.o "${srctree}/scripts/empty_paraminfo.S"
+ paraminfo_o=.tmp_paraminfo.o
+fi
+
if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then
- # The kallsyms linking does not need debug symbols, but BTF and
- # lineinfo generation do.
+ # The kallsyms linking does not need debug symbols, but BTF,
+ # lineinfo and paraminfo generation do.
if ! is_enabled CONFIG_DEBUG_INFO_BTF &&
- ! is_enabled CONFIG_KALLSYMS_LINEINFO; then
+ ! is_enabled CONFIG_KALLSYMS_LINEINFO &&
+ ! is_enabled CONFIG_KALLSYMS_PARAMINFO; then
strip_debug=1
fi
@@ -256,6 +288,10 @@ if is_enabled CONFIG_KALLSYMS_LINEINFO; then
gen_lineinfo .tmp_vmlinux1
fi
+if is_enabled CONFIG_KALLSYMS_PARAMINFO; then
+ gen_paraminfo .tmp_vmlinux1
+fi
+
if is_enabled CONFIG_KALLSYMS; then
# kallsyms support
--
2.51.0
next prev parent reply other threads:[~2026-03-23 16:49 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-23 16:48 [PATCH 0/2] kallsyms: show typed function parameters in oops/WARN dumps Sasha Levin
2026-03-23 16:48 ` Sasha Levin [this message]
2026-03-24 15:03 ` [PATCH 1/2] kallsyms: show function parameter info " Alexei Starovoitov
2026-03-24 16:00 ` Sasha Levin
2026-03-24 16:04 ` Alexei Starovoitov
2026-03-24 18:44 ` Sasha Levin
2026-03-25 1:18 ` [RFC] btf: split core BTF parsing out of BPF subsystem into kernel/btf/ Sasha Levin
2026-03-25 2:15 ` bot+bpf-ci
2026-03-28 17:49 ` kernel test robot
2026-03-28 18:21 ` kernel test robot
2026-03-28 20:19 ` kernel test robot
2026-04-22 10:44 ` Alan Maguire
2026-04-22 14:48 ` Alexei Starovoitov
2026-03-24 17:34 ` [PATCH 1/2] kallsyms: show function parameter info in oops/WARN dumps Alan Maguire
2026-03-24 18:51 ` Sasha Levin
2026-04-25 1:59 ` sashiko-bot
2026-03-23 16:48 ` [PATCH 2/2] kallsyms: add BTF-based deep parameter rendering in oops dumps Sasha Levin
2026-03-24 15:07 ` Alexei Starovoitov
2026-04-25 2:15 ` sashiko-bot
2026-03-23 18:43 ` [PATCH 0/2] kallsyms: show typed function parameters in oops/WARN dumps Alexey Dobriyan
2026-03-23 22:58 ` Sasha Levin
2026-03-23 22:50 ` Andrew Morton
2026-03-23 23:08 ` Sasha Levin
2026-03-24 8:57 ` Jiri Olsa
2026-03-24 11:39 ` Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260323164858.1939248-2-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=James.Bottomley@HansenPartnership.com \
--cc=adobriyan@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=ast@kernel.org \
--cc=bp@alien8.de \
--cc=bpf@vger.kernel.org \
--cc=corbet@lwn.net \
--cc=dave.hansen@linux.intel.com \
--cc=davidgow@google.com \
--cc=deller@gmx.de \
--cc=geert@linux-m68k.org \
--cc=gregkh@linuxfoundation.org \
--cc=hpa@zytor.com \
--cc=jgross@suse.com \
--cc=jpoimboe@kernel.org \
--cc=kees@kernel.org \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-modules@vger.kernel.org \
--cc=masahiroy@kernel.org \
--cc=mcgrof@kernel.org \
--cc=mingo@redhat.com \
--cc=nathan@kernel.org \
--cc=nsc@kernel.org \
--cc=peterz@infradead.org \
--cc=petr.pavlu@suse.com \
--cc=pmladek@suse.com \
--cc=rdunlap@infradead.org \
--cc=rostedt@goodmis.org \
--cc=tglx@kernel.org \
--cc=vbabka@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.