[PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces

Sasha Levin posted 3 patches 1 month, 1 week ago
There is a newer version of this series
[PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Sasha Levin 1 month, 1 week ago
Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
lookup table in the kernel image so stack traces directly print source
file and line number information:

  root@localhost:~# echo c > /proc/sysrq-trigger
  [   11.201987] sysrq: Trigger a crash
  [   11.202831] Kernel panic - not syncing: sysrq triggered crash
  [   11.206218] Call Trace:
  [   11.206501]  <TASK>
  [   11.206749]  dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:94)
  [   11.207403]  vpanic+0x36e/0x620 (kernel/panic.c:650)
  [   11.208565]  ? __lock_acquire+0x465/0x2240 (kernel/locking/lockdep.c:4674)
  [   11.209324]  panic+0xc9/0xd0 (kernel/panic.c:787)
  [   11.211873]  ? find_held_lock+0x2b/0x80 (kernel/locking/lockdep.c:5350)
  [   11.212597]  ? lock_release+0xd3/0x300 (kernel/locking/lockdep.c:5535)
  [   11.213312]  sysrq_handle_crash+0x1a/0x20 (drivers/tty/sysrq.c:154)
  [   11.214005]  __handle_sysrq.cold+0x66/0x256 (drivers/tty/sysrq.c:611)
  [   11.214712]  write_sysrq_trigger+0x65/0x80 (drivers/tty/sysrq.c:1221)
  [   11.215424]  proc_reg_write+0x1bd/0x3c0 (fs/proc/inode.c:330)
  [   11.216061]  vfs_write+0x1c6/0xff0 (fs/read_write.c:686)
  [   11.218848]  ksys_write+0xfa/0x200 (fs/read_write.c:740)
  [   11.222394]  do_syscall_64+0xf3/0x690 (arch/x86/entry/syscall_64.c:63)
  [   11.223942]  entry_SYSCALL_64_after_hwframe+0x77/0x7f (arch/x86/entry/entry_64.S:121)

At build time, a new host tool (scripts/gen_lineinfo) reads DWARF
.debug_line from vmlinux using libdw (elfutils), extracts all
address-to-file:line mappings, and generates an assembly file with
sorted parallel arrays (offsets from _text, file IDs, and line
numbers). These are linked into vmlinux as .rodata.

At runtime, kallsyms_lookup_lineinfo() does a binary search on the
table and __sprint_symbol() appends "(file:line)" to each stack frame.
The lookup uses offsets from _text so it works with KASLR, requires no
locks or allocations, and is safe in any context including panic.

The feature requires CONFIG_DEBUG_INFO (for DWARF data) and
elfutils (libdw-dev) on the build host.

Memory footprint measured with a 1852-option x86_64 config:

  Table: 4,597,583 entries from 4,841 source files
    lineinfo_addrs[]     4,597,583 x u32  = 17.5 MiB
    lineinfo_file_ids[]  4,597,583 x u16  =  8.8 MiB
    lineinfo_lines[]     4,597,583 x u32  = 17.5 MiB
    file_offsets + filenames              ~  0.1 MiB
    Total .rodata increase:              ~ 44.0 MiB

  vmlinux (stripped):  529 MiB -> 573 MiB  (+44 MiB / +8.3%)

Note: this probably won't be something we roll into "production", but
it might be useful for the average user given the relatively low memory
footprint, in canary deployments for hyperscalers, or by default for
folks who run tests/fuzzing/etc.

Disclaimer: this was vibe coded over an afternoon with an AI coding
assistant.

The .config used for testing is a simple KVM guest configuration for
local development and testing.

Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 Documentation/admin-guide/index.rst           |   1 +
 .../admin-guide/kallsyms-lineinfo.rst         |  72 +++
 MAINTAINERS                                   |   6 +
 include/linux/kallsyms.h                      |  32 +-
 init/Kconfig                                  |  20 +
 kernel/kallsyms.c                             |  69 +++
 kernel/kallsyms_internal.h                    |  10 +
 scripts/.gitignore                            |   1 +
 scripts/Makefile                              |   3 +
 scripts/gen_lineinfo.c                        | 444 ++++++++++++++++++
 scripts/kallsyms.c                            |  16 +
 scripts/link-vmlinux.sh                       |  66 ++-
 12 files changed, 736 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/admin-guide/kallsyms-lineinfo.rst
 create mode 100644 scripts/gen_lineinfo.c

diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst
index b734f8a2a2c48..1801b9880aeb7 100644
--- a/Documentation/admin-guide/index.rst
+++ b/Documentation/admin-guide/index.rst
@@ -73,6 +73,7 @@ problems and bugs in particular.
    ramoops
    dynamic-debug-howto
    init
+   kallsyms-lineinfo
    kdump/index
    perf/index
    pstore-blk
diff --git a/Documentation/admin-guide/kallsyms-lineinfo.rst b/Documentation/admin-guide/kallsyms-lineinfo.rst
new file mode 100644
index 0000000000000..4dffc18dbcf5a
--- /dev/null
+++ b/Documentation/admin-guide/kallsyms-lineinfo.rst
@@ -0,0 +1,72 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================
+Kallsyms Source Line Info (LINEINFO)
+==================================
+
+Overview
+========
+
+``CONFIG_KALLSYMS_LINEINFO`` embeds DWARF-derived source file and line number
+mappings into the kernel image so that stack traces include
+``(file.c:123)`` annotations next to each symbol.  This makes it significantly
+easier to pinpoint the exact source location during debugging, without needing
+to manually cross-reference addresses with ``addr2line``.
+
+Enabling the Feature
+====================
+
+Enable the following kernel configuration options::
+
+    CONFIG_KALLSYMS=y
+    CONFIG_DEBUG_INFO=y
+    CONFIG_KALLSYMS_LINEINFO=y
+
+Build dependency: the host tool ``scripts/gen_lineinfo`` requires ``libdw``
+from elfutils.  Install the development package:
+
+- Debian/Ubuntu: ``apt install libdw-dev``
+- Fedora/RHEL: ``dnf install elfutils-devel``
+- Arch Linux: ``pacman -S elfutils``
+
+Example Output
+==============
+
+Without ``CONFIG_KALLSYMS_LINEINFO``::
+
+    Call Trace:
+     <TASK>
+     dump_stack_lvl+0x5d/0x80
+     do_syscall_64+0x82/0x190
+     entry_SYSCALL_64_after_hwframe+0x76/0x7e
+
+With ``CONFIG_KALLSYMS_LINEINFO``::
+
+    Call Trace:
+     <TASK>
+     dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:123)
+     do_syscall_64+0x82/0x190 (arch/x86/entry/common.c:52)
+     entry_SYSCALL_64_after_hwframe+0x76/0x7e
+
+Note that assembly routines (such as ``entry_SYSCALL_64_after_hwframe``) are
+not annotated because they lack DWARF debug information.
+
+Memory Overhead
+===============
+
+The lineinfo tables are stored in ``.rodata`` and typically add approximately
+44 MiB to the kernel image for a standard configuration (~4.6 million DWARF
+line entries, ~10 bytes per entry after deduplication).
+
+Known Limitations
+=================
+
+- **vmlinux only**: Only symbols in the core kernel image are annotated.
+  Module symbols are not covered.
+- **4 GiB offset limit**: Address offsets from ``_text`` are stored as 32-bit
+  values.  Entries beyond 4 GiB from ``_text`` are skipped at build time with
+  a warning.
+- **65535 file limit**: Source file IDs are stored as 16-bit values.  Builds
+  with more than 65535 unique source files will fail with an error.
+- **No assembly annotations**: Functions implemented in assembly that lack
+  DWARF ``.debug_line`` data are not annotated.
diff --git a/MAINTAINERS b/MAINTAINERS
index 61bf550fd37c2..ab987e74bb0f5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14278,6 +14278,12 @@ F:	lib/Kconfig.kmsan
 F:	mm/kmsan/
 F:	scripts/Makefile.kmsan
 
+KALLSYMS LINEINFO
+M:	Sasha Levin <sashal@kernel.org>
+S:	Maintained
+F:	Documentation/admin-guide/kallsyms-lineinfo.rst
+F:	scripts/gen_lineinfo.c
+
 KPROBES
 M:	Naveen N Rao <naveen@kernel.org>
 M:	"David S. Miller" <davem@davemloft.net>
diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
index d5dd54c53ace6..e1d00e1373779 100644
--- a/include/linux/kallsyms.h
+++ b/include/linux/kallsyms.h
@@ -16,10 +16,19 @@
 #include <asm/sections.h>
 
 #define KSYM_NAME_LEN 512
+
+#ifdef CONFIG_KALLSYMS_LINEINFO
+/* Extra space for " (path/to/file.c:12345)" suffix */
+#define KSYM_LINEINFO_LEN 128
+#else
+#define KSYM_LINEINFO_LEN 0
+#endif
+
 #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s]") + \
 			(KSYM_NAME_LEN - 1) + \
 			2*(BITS_PER_LONG*3/10) + (MODULE_NAME_LEN - 1) + \
-			(BUILD_ID_SIZE_MAX * 2) + 1)
+			(BUILD_ID_SIZE_MAX * 2) + 1 + \
+			KSYM_LINEINFO_LEN)
 
 struct cred;
 struct module;
@@ -96,6 +105,19 @@ extern int sprint_backtrace_build_id(char *buffer, unsigned long address);
 
 int lookup_symbol_name(unsigned long addr, char *symname);
 
+#ifdef CONFIG_KALLSYMS_LINEINFO
+bool kallsyms_lookup_lineinfo(unsigned long addr, unsigned long sym_start,
+			      const char **file, unsigned int *line);
+#else
+static inline bool kallsyms_lookup_lineinfo(unsigned long addr,
+					    unsigned long sym_start,
+					    const char **file,
+					    unsigned int *line)
+{
+	return false;
+}
+#endif
+
 #else /* !CONFIG_KALLSYMS */
 
 static inline unsigned long kallsyms_lookup_name(const char *name)
@@ -164,6 +186,14 @@ static inline int kallsyms_on_each_match_symbol(int (*fn)(void *, unsigned long)
 {
 	return -EOPNOTSUPP;
 }
+
+static inline bool kallsyms_lookup_lineinfo(unsigned long addr,
+					    unsigned long sym_start,
+					    const char **file,
+					    unsigned int *line)
+{
+	return false;
+}
 #endif /*CONFIG_KALLSYMS*/
 
 static inline void print_ip_sym(const char *loglvl, unsigned long ip)
diff --git a/init/Kconfig b/init/Kconfig
index b55deae9256c7..c39f27e6393a8 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2050,6 +2050,26 @@ config KALLSYMS_ALL
 
 	  Say N unless you really need all symbols, or kernel live patching.
 
+config KALLSYMS_LINEINFO
+	bool "Embed source file:line information in stack traces"
+	depends on KALLSYMS && DEBUG_INFO
+	help
+	  Embeds an address-to-source-line mapping table in the kernel
+	  image so that stack traces directly include file:line information,
+	  similar to what scripts/decode_stacktrace.sh provides but without
+	  needing external tools or a vmlinux with debug info at runtime.
+
+	  When enabled, stack traces will look like:
+
+	    kmem_cache_alloc_noprof+0x60/0x630 (mm/slub.c:3456)
+	    anon_vma_clone+0x2ed/0xcf0 (mm/rmap.c:412)
+
+	  This requires elfutils (libdw-dev/elfutils-devel) on the build host.
+	  Adds approximately 44MB to a typical kernel image (10 bytes per
+	  DWARF line-table entry, ~4.6M entries for a typical config).
+
+	  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 aec2f06858afd..2b9c9d6322a3e 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -467,6 +467,62 @@ static int append_buildid(char *buffer,   const char *modname,
 
 #endif /* CONFIG_STACKTRACE_BUILD_ID */
 
+#ifdef CONFIG_KALLSYMS_LINEINFO
+bool kallsyms_lookup_lineinfo(unsigned long addr, unsigned long sym_start,
+			      const char **file, unsigned int *line)
+{
+	unsigned long long raw_offset;
+	unsigned int offset, low, high, mid, file_id;
+	unsigned long line_addr;
+
+	if (!lineinfo_num_entries)
+		return false;
+
+	/* Compute offset from _text */
+	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 largest entry <= offset */
+	low = 0;
+	high = lineinfo_num_entries;
+	while (low < high) {
+		mid = low + (high - low) / 2;
+		if (lineinfo_addrs[mid] <= offset)
+			low = mid + 1;
+		else
+			high = mid;
+	}
+
+	if (low == 0)
+		return false;
+	low--;
+
+	/*
+	 * Validate that the matched lineinfo entry belongs to the same
+	 * symbol.  Without this check, assembly routines or other
+	 * functions lacking DWARF data would inherit the file:line of
+	 * a preceding C function.
+	 */
+	line_addr = (unsigned long)_text + lineinfo_addrs[low];
+	if (line_addr < sym_start)
+		return false;
+
+	file_id = lineinfo_file_ids[low];
+	*line = lineinfo_lines[low];
+
+	if (file_id >= lineinfo_num_files)
+		return false;
+
+	*file = &lineinfo_filenames[lineinfo_file_offsets[file_id]];
+	return true;
+}
+#endif /* CONFIG_KALLSYMS_LINEINFO */
+
 /* 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)
@@ -497,6 +553,19 @@ static int __sprint_symbol(char *buffer, unsigned long address,
 		len += sprintf(buffer + len, "]");
 	}
 
+#ifdef CONFIG_KALLSYMS_LINEINFO
+	if (!modname) {
+		const char *li_file;
+		unsigned int li_line;
+		unsigned long sym_start = address - offset;
+
+		if (kallsyms_lookup_lineinfo(address, sym_start,
+					     &li_file, &li_line))
+			len += snprintf(buffer + len, KSYM_SYMBOL_LEN - len,
+					" (%s:%u)", li_file, li_line);
+	}
+#endif
+
 	return len;
 }
 
diff --git a/kernel/kallsyms_internal.h b/kernel/kallsyms_internal.h
index 81a867dbe57d4..868a1d5035212 100644
--- a/kernel/kallsyms_internal.h
+++ b/kernel/kallsyms_internal.h
@@ -15,4 +15,14 @@ extern const u16 kallsyms_token_index[];
 extern const unsigned int kallsyms_markers[];
 extern const u8 kallsyms_seqs_of_names[];
 
+#ifdef CONFIG_KALLSYMS_LINEINFO
+extern const u32 lineinfo_num_entries;
+extern const u32 lineinfo_addrs[];
+extern const u16 lineinfo_file_ids[];
+extern const u32 lineinfo_lines[];
+extern const u32 lineinfo_num_files;
+extern const u32 lineinfo_file_offsets[];
+extern const char lineinfo_filenames[];
+#endif
+
 #endif // LINUX_KALLSYMS_INTERNAL_H_
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 4215c2208f7e4..e175714c18b61 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 /asn1_compiler
+/gen_lineinfo
 /gen_packed_field_checks
 /generate_rust_target
 /insert-sys-cert
diff --git a/scripts/Makefile b/scripts/Makefile
index 0941e5ce7b575..ffe89875b3295 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -4,6 +4,7 @@
 # the kernel for the build process.
 
 hostprogs-always-$(CONFIG_KALLSYMS)			+= kallsyms
+hostprogs-always-$(CONFIG_KALLSYMS_LINEINFO)		+= gen_lineinfo
 hostprogs-always-$(BUILD_C_RECORDMCOUNT)		+= recordmcount
 hostprogs-always-$(CONFIG_BUILDTIME_TABLE_SORT)		+= sorttable
 hostprogs-always-$(CONFIG_ASN1)				+= asn1_compiler
@@ -36,6 +37,8 @@ HOSTLDLIBS_sorttable = -lpthread
 HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
 HOSTCFLAGS_sign-file.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
 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)
 
 ifdef CONFIG_UNWINDER_ORC
 ifeq ($(ARCH),x86_64)
diff --git a/scripts/gen_lineinfo.c b/scripts/gen_lineinfo.c
new file mode 100644
index 0000000000000..9eebfaca5857c
--- /dev/null
+++ b/scripts/gen_lineinfo.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * gen_lineinfo.c - Generate address-to-source-line lookup tables from DWARF
+ *
+ * Copyright (C) 2026 Sasha Levin <sashal@kernel.org>
+ *
+ * Reads DWARF .debug_line from a vmlinux ELF file and outputs an assembly
+ * file containing sorted lookup tables that the kernel uses to annotate
+ * stack traces with source file:line information.
+ *
+ * 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>
+
+static unsigned int skipped_overflow;
+
+struct line_entry {
+	unsigned int offset;	/* offset from _text */
+	unsigned int file_id;
+	unsigned int line;
+};
+
+struct file_entry {
+	char *name;
+	unsigned int id;
+	unsigned int str_offset;
+};
+
+static struct line_entry *entries;
+static unsigned int num_entries;
+static unsigned int entries_capacity;
+
+static struct file_entry *files;
+static unsigned int num_files;
+static unsigned int files_capacity;
+
+#define FILE_HASH_BITS 13
+#define FILE_HASH_SIZE (1 << FILE_HASH_BITS)
+
+struct file_hash_entry {
+	const char *name;
+	unsigned int id;
+};
+
+static struct file_hash_entry file_hash[FILE_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 & (FILE_HASH_SIZE - 1);
+}
+
+static void add_entry(unsigned int offset, unsigned int file_id,
+		      unsigned int line)
+{
+	if (num_entries >= entries_capacity) {
+		entries_capacity = entries_capacity ? entries_capacity * 2 : 65536;
+		entries = realloc(entries, entries_capacity * sizeof(*entries));
+		if (!entries) {
+			fprintf(stderr, "out of memory\n");
+			exit(1);
+		}
+	}
+	entries[num_entries].offset = offset;
+	entries[num_entries].file_id = file_id;
+	entries[num_entries].line = line;
+	num_entries++;
+}
+
+static unsigned int find_or_add_file(const char *name)
+{
+	unsigned int h = hash_str(name);
+
+	/* Open-addressing lookup with linear probing */
+	while (file_hash[h].name) {
+		if (!strcmp(file_hash[h].name, name))
+			return file_hash[h].id;
+		h = (h + 1) & (FILE_HASH_SIZE - 1);
+	}
+
+	if (num_files >= 65535) {
+		fprintf(stderr,
+			"gen_lineinfo: too many source files (%u > 65535)\n",
+			num_files);
+		exit(1);
+	}
+
+	if (num_files >= files_capacity) {
+		files_capacity = files_capacity ? files_capacity * 2 : 4096;
+		files = realloc(files, files_capacity * sizeof(*files));
+		if (!files) {
+			fprintf(stderr, "out of memory\n");
+			exit(1);
+		}
+	}
+	files[num_files].name = strdup(name);
+	files[num_files].id = num_files;
+
+	/* Insert into hash table (points to files[] entry) */
+	file_hash[h].name = files[num_files].name;
+	file_hash[h].id = num_files;
+
+	num_files++;
+	return num_files - 1;
+}
+
+/*
+ * Strip a filename to a kernel-relative path.
+ *
+ * For absolute paths, strip the comp_dir prefix (from DWARF) to get
+ * a kernel-tree-relative path, or fall back to the basename.
+ */
+static const char *make_relative(const char *path, const char *comp_dir)
+{
+	const char *p;
+
+	/* If already relative, use as-is */
+	if (path[0] != '/')
+		return path;
+
+	/* comp_dir from DWARF is the most reliable method */
+	if (comp_dir) {
+		size_t len = strlen(comp_dir);
+
+		if (!strncmp(path, comp_dir, len) && path[len] == '/')
+			return path + len + 1;
+	}
+
+	/* Fall back to basename */
+	p = strrchr(path, '/');
+	return p ? p + 1 : path;
+}
+
+static int compare_entries(const void *a, const void *b)
+{
+	const struct line_entry *ea = a;
+	const struct line_entry *eb = b;
+
+	if (ea->offset != eb->offset)
+		return ea->offset < eb->offset ? -1 : 1;
+	if (ea->file_id != eb->file_id)
+		return ea->file_id < eb->file_id ? -1 : 1;
+	if (ea->line != eb->line)
+		return ea->line < eb->line ? -1 : 1;
+	return 0;
+}
+
+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 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;
+		Dwarf_Lines *lines;
+		size_t nlines;
+		Dwarf_Attribute attr;
+		const char *comp_dir = NULL;
+
+		if (!dwarf_offdie(dwarf, off + hdr_size, &cudie))
+			goto next;
+
+		if (dwarf_attr(&cudie, DW_AT_comp_dir, &attr))
+			comp_dir = dwarf_formstring(&attr);
+
+		if (dwarf_getsrclines(&cudie, &lines, &nlines) != 0)
+			goto next;
+
+		for (size_t i = 0; i < nlines; i++) {
+			Dwarf_Line *line = dwarf_onesrcline(lines, i);
+			Dwarf_Addr addr;
+			const char *src;
+			const char *rel;
+			unsigned int file_id, loffset;
+			int lineno;
+
+			if (!line)
+				continue;
+
+			if (dwarf_lineaddr(line, &addr) != 0)
+				continue;
+			if (dwarf_lineno(line, &lineno) != 0)
+				continue;
+			if (lineno == 0)
+				continue;
+
+			src = dwarf_linesrc(line, NULL, NULL);
+			if (!src)
+				continue;
+
+			if (addr < text_addr)
+				continue;
+
+			{
+				unsigned long long raw_offset = addr - text_addr;
+
+				if (raw_offset > UINT_MAX) {
+					skipped_overflow++;
+					continue;
+				}
+				loffset = (unsigned int)raw_offset;
+			}
+
+			rel = make_relative(src, comp_dir);
+			file_id = find_or_add_file(rel);
+
+			add_entry(loffset, file_id, (unsigned int)lineno);
+		}
+next:
+		off = next_off;
+	}
+}
+
+static void deduplicate(void)
+{
+	unsigned int i, j;
+
+	if (num_entries < 2)
+		return;
+
+	/* Sort by offset, then file_id, then line for stability */
+	qsort(entries, num_entries, sizeof(*entries), compare_entries);
+
+	/*
+	 * Remove duplicate entries:
+	 * - Same offset: keep first (deterministic from stable sort keys)
+	 * - Same file:line as previous kept entry: redundant for binary
+	 *   search -- any address between them resolves to the earlier one
+	 */
+	j = 0;
+	for (i = 1; i < num_entries; i++) {
+		if (entries[i].offset == entries[j].offset)
+			continue;
+		if (entries[i].file_id == entries[j].file_id &&
+		    entries[i].line == entries[j].line)
+			continue;
+		j++;
+		if (j != i)
+			entries[j] = entries[i];
+	}
+	num_entries = j + 1;
+}
+
+static void compute_file_offsets(void)
+{
+	unsigned int offset = 0;
+
+	for (unsigned int i = 0; i < num_files; i++) {
+		files[i].str_offset = offset;
+		offset += strlen(files[i].name) + 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)
+{
+	printf("/* SPDX-License-Identifier: GPL-2.0 */\n");
+	printf("/*\n");
+	printf(" * Automatically generated by scripts/gen_lineinfo\n");
+	printf(" * Do not edit.\n");
+	printf(" */\n\n");
+
+	printf("\t.section .rodata, \"a\"\n\n");
+
+	/* Number of entries */
+	printf("\t.globl lineinfo_num_entries\n");
+	printf("\t.balign 4\n");
+	printf("lineinfo_num_entries:\n");
+	printf("\t.long %u\n\n", num_entries);
+
+	/* Number of files */
+	printf("\t.globl lineinfo_num_files\n");
+	printf("\t.balign 4\n");
+	printf("lineinfo_num_files:\n");
+	printf("\t.long %u\n\n", num_files);
+
+	/* Sorted address offsets from _text */
+	printf("\t.globl lineinfo_addrs\n");
+	printf("\t.balign 4\n");
+	printf("lineinfo_addrs:\n");
+	for (unsigned int i = 0; i < num_entries; i++)
+		printf("\t.long 0x%x\n", entries[i].offset);
+	printf("\n");
+
+	/* File IDs, parallel to addrs (u16 -- supports up to 65535 files) */
+	printf("\t.globl lineinfo_file_ids\n");
+	printf("\t.balign 2\n");
+	printf("lineinfo_file_ids:\n");
+	for (unsigned int i = 0; i < num_entries; i++)
+		printf("\t.short %u\n", entries[i].file_id);
+	printf("\n");
+
+	/* Line numbers, parallel to addrs */
+	printf("\t.globl lineinfo_lines\n");
+	printf("\t.balign 4\n");
+	printf("lineinfo_lines:\n");
+	for (unsigned int i = 0; i < num_entries; i++)
+		printf("\t.long %u\n", entries[i].line);
+	printf("\n");
+
+	/* File string offset table */
+	printf("\t.globl lineinfo_file_offsets\n");
+	printf("\t.balign 4\n");
+	printf("lineinfo_file_offsets:\n");
+	for (unsigned int i = 0; i < num_files; i++)
+		printf("\t.long %u\n", files[i].str_offset);
+	printf("\n");
+
+	/* Concatenated NUL-terminated filenames */
+	printf("\t.globl lineinfo_filenames\n");
+	printf("lineinfo_filenames:\n");
+	for (unsigned int i = 0; i < num_files; i++)
+		print_escaped_asciz(files[i].name);
+	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>\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);
+
+	if (skipped_overflow)
+		fprintf(stderr,
+			"lineinfo: warning: %u entries skipped (offset > 4 GiB from _text)\n",
+			skipped_overflow);
+
+	deduplicate();
+	compute_file_offsets();
+
+	fprintf(stderr, "lineinfo: %u entries, %u files\n",
+		num_entries, num_files);
+
+	output_assembly();
+
+	dwarf_end(dwarf);
+	elf_end(elf);
+	close(fd);
+
+	/* Cleanup */
+	free(entries);
+	for (unsigned int i = 0; i < num_files; i++)
+		free(files[i].name);
+	free(files);
+
+	return 0;
+}
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 37d5c095ad22a..42662c4fbc6c9 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -78,6 +78,17 @@ static char *sym_name(const struct sym_entry *s)
 
 static bool is_ignored_symbol(const char *name, char type)
 {
+	/* Ignore lineinfo symbols for kallsyms pass stability */
+	static const char * const lineinfo_syms[] = {
+		"lineinfo_addrs",
+		"lineinfo_file_ids",
+		"lineinfo_file_offsets",
+		"lineinfo_filenames",
+		"lineinfo_lines",
+		"lineinfo_num_entries",
+		"lineinfo_num_files",
+	};
+
 	if (type == 'u' || type == 'n')
 		return true;
 
@@ -90,6 +101,11 @@ static bool is_ignored_symbol(const char *name, char type)
 			return true;
 	}
 
+	for (size_t i = 0; i < ARRAY_SIZE(lineinfo_syms); i++) {
+		if (!strcmp(name, lineinfo_syms[i]))
+			return true;
+	}
+
 	return false;
 }
 
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index f99e196abeea4..640209f2e9eb9 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} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
+		${kallsymso} ${lineinfo_o} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
 }
 
 # Create ${2}.o file with all symbols from the ${1} object file
@@ -129,6 +129,26 @@ kallsyms()
 	kallsymso=${2}.o
 }
 
+# Generate lineinfo tables from DWARF debug info in a temporary vmlinux.
+# ${1} - temporary vmlinux with debug info
+# Output: sets lineinfo_o to the generated .o file
+gen_lineinfo()
+{
+	info LINEINFO .tmp_lineinfo.S
+	if ! scripts/gen_lineinfo "${1}" > .tmp_lineinfo.S; then
+		echo >&2 "Failed to generate lineinfo from ${1}"
+		echo >&2 "Try to disable CONFIG_KALLSYMS_LINEINFO"
+		exit 1
+	fi
+
+	info AS .tmp_lineinfo.o
+	${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \
+	      ${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
+	      -c -o .tmp_lineinfo.o .tmp_lineinfo.S
+
+	lineinfo_o=.tmp_lineinfo.o
+}
+
 # Perform kallsyms for the given temporary vmlinux.
 sysmap_and_kallsyms()
 {
@@ -155,6 +175,7 @@ sorttable()
 cleanup()
 {
 	rm -f .btf.*
+	rm -f .tmp_lineinfo.*
 	rm -f .tmp_vmlinux.nm-sort
 	rm -f System.map
 	rm -f vmlinux
@@ -183,6 +204,7 @@ fi
 btf_vmlinux_bin_o=
 btfids_vmlinux=
 kallsymso=
+lineinfo_o=
 strip_debug=
 generate_map=
 
@@ -198,10 +220,44 @@ if is_enabled CONFIG_KALLSYMS; then
 	kallsyms .tmp_vmlinux0.syms .tmp_vmlinux0.kallsyms
 fi
 
+if is_enabled CONFIG_KALLSYMS_LINEINFO; then
+	# Generate a dummy empty lineinfo object for the initial link,
+	# same pattern as the dummy kallsyms above.  The real lineinfo
+	# is generated from .tmp_vmlinux1 after it has been linked with
+	# debug info.
+	cat > .tmp_lineinfo.S <<'EOAS'
+	.section .rodata, "a"
+	.globl lineinfo_num_entries
+	.balign 4
+lineinfo_num_entries:
+	.long 0
+	.globl lineinfo_num_files
+	.balign 4
+lineinfo_num_files:
+	.long 0
+	.globl lineinfo_addrs
+lineinfo_addrs:
+	.globl lineinfo_file_ids
+lineinfo_file_ids:
+	.globl lineinfo_lines
+lineinfo_lines:
+	.globl lineinfo_file_offsets
+lineinfo_file_offsets:
+	.globl lineinfo_filenames
+lineinfo_filenames:
+EOAS
+	${CC} ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS} \
+	      ${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
+	      -c -o .tmp_lineinfo.o .tmp_lineinfo.S
+	lineinfo_o=.tmp_lineinfo.o
+fi
+
 if is_enabled CONFIG_KALLSYMS || is_enabled CONFIG_DEBUG_INFO_BTF; then
 
-	# The kallsyms linking does not need debug symbols, but the BTF does.
-	if ! is_enabled CONFIG_DEBUG_INFO_BTF; then
+	# The kallsyms linking does not need debug symbols, but BTF and
+	# lineinfo generation do.
+	if ! is_enabled CONFIG_DEBUG_INFO_BTF &&
+	   ! is_enabled CONFIG_KALLSYMS_LINEINFO; then
 		strip_debug=1
 	fi
 
@@ -219,6 +275,10 @@ if is_enabled CONFIG_DEBUG_INFO_BTF; then
 	btfids_vmlinux=.tmp_vmlinux1.BTF_ids
 fi
 
+if is_enabled CONFIG_KALLSYMS_LINEINFO; then
+	gen_lineinfo .tmp_vmlinux1
+fi
+
 if is_enabled CONFIG_KALLSYMS; then
 
 	# kallsyms support
-- 
2.51.0
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Petr Mladek 1 month ago
On Tue 2026-03-03 13:21:01, Sasha Levin wrote:
> Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
> lookup table in the kernel image so stack traces directly print source
> file and line number information:
> 
>   root@localhost:~# echo c > /proc/sysrq-trigger
>   [   11.201987] sysrq: Trigger a crash
>   [   11.202831] Kernel panic - not syncing: sysrq triggered crash
>   [   11.206218] Call Trace:
>   [   11.206501]  <TASK>
>   [   11.206749]  dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:94)
>   [   11.207403]  vpanic+0x36e/0x620 (kernel/panic.c:650)
>   [   11.208565]  ? __lock_acquire+0x465/0x2240 (kernel/locking/lockdep.c:4674)
>   [   11.209324]  panic+0xc9/0xd0 (kernel/panic.c:787)
>   [   11.211873]  ? find_held_lock+0x2b/0x80 (kernel/locking/lockdep.c:5350)
>   [   11.212597]  ? lock_release+0xd3/0x300 (kernel/locking/lockdep.c:5535)
>   [   11.213312]  sysrq_handle_crash+0x1a/0x20 (drivers/tty/sysrq.c:154)
>   [   11.214005]  __handle_sysrq.cold+0x66/0x256 (drivers/tty/sysrq.c:611)
>   [   11.214712]  write_sysrq_trigger+0x65/0x80 (drivers/tty/sysrq.c:1221)
>   [   11.215424]  proc_reg_write+0x1bd/0x3c0 (fs/proc/inode.c:330)
>   [   11.216061]  vfs_write+0x1c6/0xff0 (fs/read_write.c:686)
>   [   11.218848]  ksys_write+0xfa/0x200 (fs/read_write.c:740)
>   [   11.222394]  do_syscall_64+0xf3/0x690 (arch/x86/entry/syscall_64.c:63)
>   [   11.223942]  entry_SYSCALL_64_after_hwframe+0x77/0x7f (arch/x86/entry/entry_64.S:121)
> 
> --- a/include/linux/kallsyms.h
> +++ b/include/linux/kallsyms.h
> @@ -16,10 +16,19 @@
>  #include <asm/sections.h>
>  
>  #define KSYM_NAME_LEN 512
> +
> +#ifdef CONFIG_KALLSYMS_LINEINFO
> +/* Extra space for " (path/to/file.c:12345)" suffix */
> +#define KSYM_LINEINFO_LEN 128
> +#else
> +#define KSYM_LINEINFO_LEN 0
> +#endif
> +
>  #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s]") + \

I guess that this is used also in ftrace where there formatting
is delayed. We might want:

  #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s] (%s:%u)") + \

>  			(KSYM_NAME_LEN - 1) + \
>  			2*(BITS_PER_LONG*3/10) + (MODULE_NAME_LEN - 1) + \
> -			(BUILD_ID_SIZE_MAX * 2) + 1)
> +			(BUILD_ID_SIZE_MAX * 2) + 1 + \
> +			KSYM_LINEINFO_LEN)
>  
>  struct cred;
>  struct module;
> --- a/kernel/kallsyms.c
> +++ b/kernel/kallsyms.c
> @@ -467,6 +467,62 @@ static int append_buildid(char *buffer,   const char *modname,
>  
>  #endif /* CONFIG_STACKTRACE_BUILD_ID */
>  
> +#ifdef CONFIG_KALLSYMS_LINEINFO
> +bool kallsyms_lookup_lineinfo(unsigned long addr, unsigned long sym_start,
> +			      const char **file, unsigned int *line)
> +{
> +	unsigned long long raw_offset;
> +	unsigned int offset, low, high, mid, file_id;
> +	unsigned long line_addr;
> +
> +	if (!lineinfo_num_entries)
> +		return false;
> +
> +	/* Compute offset from _text */
> +	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 largest entry <= offset */
> +	low = 0;
> +	high = lineinfo_num_entries;
> +	while (low < high) {
> +		mid = low + (high - low) / 2;
> +		if (lineinfo_addrs[mid] <= offset)
> +			low = mid + 1;
> +		else
> +			high = mid;
> +	}
> +
> +	if (low == 0)
> +		return false;
> +	low--;
> +
> +	/*
> +	 * Validate that the matched lineinfo entry belongs to the same
> +	 * symbol.  Without this check, assembly routines or other
> +	 * functions lacking DWARF data would inherit the file:line of
> +	 * a preceding C function.
> +	 */
> +	line_addr = (unsigned long)_text + lineinfo_addrs[low];
> +	if (line_addr < sym_start)
> +		return false;

This is suspicious. The binary search does "low = mid + 1".
I would expect that lineinfo_addrs[low] would point to
a higher address when the exact match is not found.

Anyway, I think that we should accept only the exact match and do:

	if (lineinfo_addrs[low] != offset)
		return false;

Or do I miss something? (Friday evening here ;-)

> +	file_id = lineinfo_file_ids[low];
> +	*line = lineinfo_lines[low];
> +
> +	if (file_id >= lineinfo_num_files)
> +		return false;
> +
> +	*file = &lineinfo_filenames[lineinfo_file_offsets[file_id]];
> +	return true;
> +}
> +#endif /* CONFIG_KALLSYMS_LINEINFO */
> +
>  /* 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)
> @@ -497,6 +553,19 @@ static int __sprint_symbol(char *buffer, unsigned long address,
>  		len += sprintf(buffer + len, "]");
>  	}
>  
> +#ifdef CONFIG_KALLSYMS_LINEINFO
> +	if (!modname) {
> +		const char *li_file;
> +		unsigned int li_line;
> +		unsigned long sym_start = address - offset;
> +
> +		if (kallsyms_lookup_lineinfo(address, sym_start,
> +					     &li_file, &li_line))
> +			len += snprintf(buffer + len, KSYM_SYMBOL_LEN - len,

s/KSYM_SYMBOL_LEN/KSYM_LINEINFO_LEN/

> +					" (%s:%u)", li_file, li_line);
> +	}
> +#endif
> +
>  	return len;
>  }


I was rather curious how the code looked like and the mentioned things
caught my eyes. And I focused on the kernel/kallsyms code.

Unfortunately, I do not have time for a proper full review at the
moment.

The code seems to work. And it generates relative paths for me, for example:

[  305.678609] sysrq: Show backtrace of all active CPUs
[  305.680615] NMI backtrace for cpu 0
[  305.680620] CPU: 0 UID: 0 PID: 1540 Comm: bash Kdump: loaded Not tainted 7.0.0-rc2-default+ #561 PREEMPT(full)  0d0ba470fd9bf64113a65472ab47c033a2658d88
[  305.680626] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.17.0-2-g4f253b9b-prebuilt.qemu.org 04/01/2014
[  305.680628] Call Trace:
[  305.680631]  <TASK>
[  305.680640]  dump_stack_lvl+0x6c/0xa0 (lib/dump_stack.c:94)
[  305.680680]  nmi_cpu_backtrace.cold+0x51/0x6a (lib/nmi_backtrace.c:113)
[  305.680689]  ? __pfx_nmi_raise_cpu_backtrace+0x10/0x10
[  305.680702]  nmi_trigger_cpumask_backtrace+0x113/0x130 (lib/nmi_backtrace.c:62)
[  305.680720]  __handle_sysrq.cold+0x9b/0xde (drivers/tty/sysrq.c:611)
[  305.680734]  write_sysrq_trigger+0x6a/0xb0 (drivers/tty/sysrq.c:1221)
[  305.680750]  proc_reg_write+0x59/0xa0 (fs/proc/inode.c:330)
[  305.680763]  vfs_write+0xd0/0x570 (fs/read_write.c:686)
[  305.680771]  ? srso_alias_return_thunk+0x5/0xfbef5 (arch/x86/lib/retpoline.S:220)
[  305.680776]  ? srso_alias_return_thunk+0x5/0xfbef5 (arch/x86/lib/retpoline.S:220)
[  305.680779]  ? __lock_release.isra.0+0x1c9/0x300 (kernel/locking/lockdep.c:342)
[  305.680796]  ? srso_alias_return_thunk+0x5/0xfbef5 (arch/x86/lib/retpoline.S:220)
[  305.680813]  ksys_write+0x70/0xf0 (fs/read_write.c:738)
[  305.680826]  do_syscall_64+0x11d/0x660 (arch/x86/entry/syscall_64.c:63)
[  305.680832]  ? irqentry_exit+0x94/0x5f0 (./include/linux/irq-entry-common.h:298)
[  305.680846]  entry_SYSCALL_64_after_hwframe+0x76/0x7e (arch/x86/entry/entry_64.S:121)

HTH,
Petr
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Sasha Levin 1 month ago
On Fri, Mar 06, 2026 at 05:36:36PM +0100, Petr Mladek wrote:
>On Tue 2026-03-03 13:21:01, Sasha Levin wrote:
>> Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
>> lookup table in the kernel image so stack traces directly print source
>> file and line number information:
>>
>>   root@localhost:~# echo c > /proc/sysrq-trigger
>>   [   11.201987] sysrq: Trigger a crash
>>   [   11.202831] Kernel panic - not syncing: sysrq triggered crash
>>   [   11.206218] Call Trace:
>>   [   11.206501]  <TASK>
>>   [   11.206749]  dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:94)
>>   [   11.207403]  vpanic+0x36e/0x620 (kernel/panic.c:650)
>>   [   11.208565]  ? __lock_acquire+0x465/0x2240 (kernel/locking/lockdep.c:4674)
>>   [   11.209324]  panic+0xc9/0xd0 (kernel/panic.c:787)
>>   [   11.211873]  ? find_held_lock+0x2b/0x80 (kernel/locking/lockdep.c:5350)
>>   [   11.212597]  ? lock_release+0xd3/0x300 (kernel/locking/lockdep.c:5535)
>>   [   11.213312]  sysrq_handle_crash+0x1a/0x20 (drivers/tty/sysrq.c:154)
>>   [   11.214005]  __handle_sysrq.cold+0x66/0x256 (drivers/tty/sysrq.c:611)
>>   [   11.214712]  write_sysrq_trigger+0x65/0x80 (drivers/tty/sysrq.c:1221)
>>   [   11.215424]  proc_reg_write+0x1bd/0x3c0 (fs/proc/inode.c:330)
>>   [   11.216061]  vfs_write+0x1c6/0xff0 (fs/read_write.c:686)
>>   [   11.218848]  ksys_write+0xfa/0x200 (fs/read_write.c:740)
>>   [   11.222394]  do_syscall_64+0xf3/0x690 (arch/x86/entry/syscall_64.c:63)
>>   [   11.223942]  entry_SYSCALL_64_after_hwframe+0x77/0x7f (arch/x86/entry/entry_64.S:121)
>>
>> --- a/include/linux/kallsyms.h
>> +++ b/include/linux/kallsyms.h
>> @@ -16,10 +16,19 @@
>>  #include <asm/sections.h>
>>
>>  #define KSYM_NAME_LEN 512
>> +
>> +#ifdef CONFIG_KALLSYMS_LINEINFO
>> +/* Extra space for " (path/to/file.c:12345)" suffix */
>> +#define KSYM_LINEINFO_LEN 128
>> +#else
>> +#define KSYM_LINEINFO_LEN 0
>> +#endif
>> +
>>  #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s]") + \
>
>I guess that this is used also in ftrace where there formatting
>is delayed. We might want:
>
>  #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s] (%s:%u)") + \

KSYM_LINEINFO_LEN already covers the full expansion of the path and line
number, not just the literal format characters. ftrace stores raw addresses and
formats via %pS at print time into a KSYM_SYMBOL_LEN-sized buffer, so there
shouldn't be an issue here.

>>  			(KSYM_NAME_LEN - 1) + \
>>  			2*(BITS_PER_LONG*3/10) + (MODULE_NAME_LEN - 1) + \
>> -			(BUILD_ID_SIZE_MAX * 2) + 1)
>> +			(BUILD_ID_SIZE_MAX * 2) + 1 + \
>> +			KSYM_LINEINFO_LEN)
>>
>>  struct cred;
>>  struct module;
>> --- a/kernel/kallsyms.c
>> +++ b/kernel/kallsyms.c
>> @@ -467,6 +467,62 @@ static int append_buildid(char *buffer,   const char *modname,
>>
>>  #endif /* CONFIG_STACKTRACE_BUILD_ID */
>>
>> +#ifdef CONFIG_KALLSYMS_LINEINFO
>> +bool kallsyms_lookup_lineinfo(unsigned long addr, unsigned long sym_start,
>> +			      const char **file, unsigned int *line)
>> +{
>> +	unsigned long long raw_offset;
>> +	unsigned int offset, low, high, mid, file_id;
>> +	unsigned long line_addr;
>> +
>> +	if (!lineinfo_num_entries)
>> +		return false;
>> +
>> +	/* Compute offset from _text */
>> +	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 largest entry <= offset */
>> +	low = 0;
>> +	high = lineinfo_num_entries;
>> +	while (low < high) {
>> +		mid = low + (high - low) / 2;
>> +		if (lineinfo_addrs[mid] <= offset)
>> +			low = mid + 1;
>> +		else
>> +			high = mid;
>> +	}
>> +
>> +	if (low == 0)
>> +		return false;
>> +	low--;
>> +
>> +	/*
>> +	 * Validate that the matched lineinfo entry belongs to the same
>> +	 * symbol.  Without this check, assembly routines or other
>> +	 * functions lacking DWARF data would inherit the file:line of
>> +	 * a preceding C function.
>> +	 */
>> +	line_addr = (unsigned long)_text + lineinfo_addrs[low];
>> +	if (line_addr < sym_start)
>> +		return false;
>
>This is suspicious. The binary search does "low = mid + 1".
>I would expect that lineinfo_addrs[low] would point to
>a higher address when the exact match is not found.
>
>Anyway, I think that we should accept only the exact match and do:
>
>	if (lineinfo_addrs[low] != offset)
>		return false;
>
>Or do I miss something? (Friday evening here ;-)

Right, when there's no exact match, low ends up pointing to the next higher
entry. The table is sparse, with one entry per source-line transition, not per
instruction address. The correct result for a given PC is the nearest
preceding entry, so the code uses low - 1 in that case. Same semantics as
kallsyms symbol lookup.

>> +	file_id = lineinfo_file_ids[low];
>> +	*line = lineinfo_lines[low];
>> +
>> +	if (file_id >= lineinfo_num_files)
>> +		return false;
>> +
>> +	*file = &lineinfo_filenames[lineinfo_file_offsets[file_id]];
>> +	return true;
>> +}
>> +#endif /* CONFIG_KALLSYMS_LINEINFO */
>> +
>>  /* 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)
>> @@ -497,6 +553,19 @@ static int __sprint_symbol(char *buffer, unsigned long address,
>>  		len += sprintf(buffer + len, "]");
>>  	}
>>
>> +#ifdef CONFIG_KALLSYMS_LINEINFO
>> +	if (!modname) {
>> +		const char *li_file;
>> +		unsigned int li_line;
>> +		unsigned long sym_start = address - offset;
>> +
>> +		if (kallsyms_lookup_lineinfo(address, sym_start,
>> +					     &li_file, &li_line))
>> +			len += snprintf(buffer + len, KSYM_SYMBOL_LEN - len,
>
>s/KSYM_SYMBOL_LEN/KSYM_LINEINFO_LEN/

KSYM_SYMBOL_LEN - len is the remaining capacity of the output buffer.  len
tracks total bytes written (symbol + offset + module + buildid), which can
easily exceed 128.

>> +					" (%s:%u)", li_file, li_line);
>> +	}
>> +#endif
>> +
>>  	return len;
>>  }
>
>
>I was rather curious how the code looked like and the mentioned things
>caught my eyes. And I focused on the kernel/kallsyms code.
>
>Unfortunately, I do not have time for a proper full review at the
>moment.
>
>The code seems to work. And it generates relative paths for me, for example:
>
>[  305.678609] sysrq: Show backtrace of all active CPUs
>[  305.680615] NMI backtrace for cpu 0
>[  305.680620] CPU: 0 UID: 0 PID: 1540 Comm: bash Kdump: loaded Not tainted 7.0.0-rc2-default+ #561 PREEMPT(full)  0d0ba470fd9bf64113a65472ab47c033a2658d88
>[  305.680626] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.17.0-2-g4f253b9b-prebuilt.qemu.org 04/01/2014
>[  305.680628] Call Trace:
>[  305.680631]  <TASK>
>[  305.680640]  dump_stack_lvl+0x6c/0xa0 (lib/dump_stack.c:94)
>[  305.680680]  nmi_cpu_backtrace.cold+0x51/0x6a (lib/nmi_backtrace.c:113)
>[  305.680689]  ? __pfx_nmi_raise_cpu_backtrace+0x10/0x10
>[  305.680702]  nmi_trigger_cpumask_backtrace+0x113/0x130 (lib/nmi_backtrace.c:62)
>[  305.680720]  __handle_sysrq.cold+0x9b/0xde (drivers/tty/sysrq.c:611)
>[  305.680734]  write_sysrq_trigger+0x6a/0xb0 (drivers/tty/sysrq.c:1221)
>[  305.680750]  proc_reg_write+0x59/0xa0 (fs/proc/inode.c:330)
>[  305.680763]  vfs_write+0xd0/0x570 (fs/read_write.c:686)
>[  305.680771]  ? srso_alias_return_thunk+0x5/0xfbef5 (arch/x86/lib/retpoline.S:220)
>[  305.680776]  ? srso_alias_return_thunk+0x5/0xfbef5 (arch/x86/lib/retpoline.S:220)
>[  305.680779]  ? __lock_release.isra.0+0x1c9/0x300 (kernel/locking/lockdep.c:342)
>[  305.680796]  ? srso_alias_return_thunk+0x5/0xfbef5 (arch/x86/lib/retpoline.S:220)
>[  305.680813]  ksys_write+0x70/0xf0 (fs/read_write.c:738)
>[  305.680826]  do_syscall_64+0x11d/0x660 (arch/x86/entry/syscall_64.c:63)
>[  305.680832]  ? irqentry_exit+0x94/0x5f0 (./include/linux/irq-entry-common.h:298)
>[  305.680846]  entry_SYSCALL_64_after_hwframe+0x76/0x7e (arch/x86/entry/entry_64.S:121)

Thanks for the review and testing! Have a great weekend!

-- 
Thanks,
Sasha
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Petr Mladek 1 month ago
On Fri 2026-03-06 12:14:45, Sasha Levin wrote:
> On Fri, Mar 06, 2026 at 05:36:36PM +0100, Petr Mladek wrote:
> > On Tue 2026-03-03 13:21:01, Sasha Levin wrote:
> > > Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
> > > lookup table in the kernel image so stack traces directly print source
> > > file and line number information:
> > > 
> > > --- a/include/linux/kallsyms.h
> > > +++ b/include/linux/kallsyms.h
> > > @@ -16,10 +16,19 @@
> > >  #include <asm/sections.h>
> > > 
> > >  #define KSYM_NAME_LEN 512
> > > +
> > > +#ifdef CONFIG_KALLSYMS_LINEINFO
> > > +/* Extra space for " (path/to/file.c:12345)" suffix */
> > > +#define KSYM_LINEINFO_LEN 128
> > > +#else
> > > +#define KSYM_LINEINFO_LEN 0
> > > +#endif
> > > +
> > >  #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s]") + \
> > 
> > I guess that this is used also in ftrace where there formatting
> > is delayed. We might want:
> > 
> >  #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s] (%s:%u)") + \
> 
> KSYM_LINEINFO_LEN already covers the full expansion of the path and line
> number, not just the literal format characters. ftrace stores raw addresses and
> formats via %pS at print time into a KSYM_SYMBOL_LEN-sized buffer, so there
> shouldn't be an issue here.

I was curious why the sizeof("%s+%#lx/%#lx [%s %s]") was there.
It did not make much sense to count some "random" part of the
format string.

I expected that it was related to the ftrace delayed formatting.
But they are written to the tracing buffer, see trace_vbprintk().

But I believe that it does not need to be counted. It seems to be some
cargo-cult programming. The size has been counted first by the commit
d069cf94ca296b7fb ("kallsyms for new modules") back in v2.6.12-rc2,
see
https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=d069cf94ca296b7fb4c7e362e8f27e2c8aca70f1
And it seems that it was not needed there.

That said, we could not simply remove it witout revisiting the rest of
the computation. Especilly, we need to make sure that it counts all
extra characters, like spaces, brackets, and the trailing '\0'.

Ideally, we should replace the unsafe sprintf() with snprintf() in
all users. (>> TODO ;-)

> > >  			(KSYM_NAME_LEN - 1) + \
> > >  			2*(BITS_PER_LONG*3/10) + (MODULE_NAME_LEN - 1) + \
> > > -			(BUILD_ID_SIZE_MAX * 2) + 1)
> > > +			(BUILD_ID_SIZE_MAX * 2) + 1 + \
> > > +			KSYM_LINEINFO_LEN)
> > > 
> > >  struct cred;
> > >  struct module;

Best Regards,
Petr
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Sasha Levin 4 weeks, 1 day ago
On Tue, Mar 10, 2026 at 04:20:32PM +0100, Petr Mladek wrote:
>On Fri 2026-03-06 12:14:45, Sasha Levin wrote:
>> On Fri, Mar 06, 2026 at 05:36:36PM +0100, Petr Mladek wrote:
>> > On Tue 2026-03-03 13:21:01, Sasha Levin wrote:
>> > > Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
>> > > lookup table in the kernel image so stack traces directly print source
>> > > file and line number information:
>> > >
>> > > --- a/include/linux/kallsyms.h
>> > > +++ b/include/linux/kallsyms.h
>> > > @@ -16,10 +16,19 @@
>> > >  #include <asm/sections.h>
>> > >
>> > >  #define KSYM_NAME_LEN 512
>> > > +
>> > > +#ifdef CONFIG_KALLSYMS_LINEINFO
>> > > +/* Extra space for " (path/to/file.c:12345)" suffix */
>> > > +#define KSYM_LINEINFO_LEN 128
>> > > +#else
>> > > +#define KSYM_LINEINFO_LEN 0
>> > > +#endif
>> > > +
>> > >  #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s]") + \
>> >
>> > I guess that this is used also in ftrace where there formatting
>> > is delayed. We might want:
>> >
>> >  #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s %s] (%s:%u)") + \
>>
>> KSYM_LINEINFO_LEN already covers the full expansion of the path and line
>> number, not just the literal format characters. ftrace stores raw addresses and
>> formats via %pS at print time into a KSYM_SYMBOL_LEN-sized buffer, so there
>> shouldn't be an issue here.
>
>I was curious why the sizeof("%s+%#lx/%#lx [%s %s]") was there.
>It did not make much sense to count some "random" part of the
>format string.
>
>I expected that it was related to the ftrace delayed formatting.
>But they are written to the tracing buffer, see trace_vbprintk().
>
>But I believe that it does not need to be counted. It seems to be some
>cargo-cult programming. The size has been counted first by the commit
>d069cf94ca296b7fb ("kallsyms for new modules") back in v2.6.12-rc2,
>see
>https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=d069cf94ca296b7fb4c7e362e8f27e2c8aca70f1
>And it seems that it was not needed there.
>
>That said, we could not simply remove it witout revisiting the rest of
>the computation. Especilly, we need to make sure that it counts all
>extra characters, like spaces, brackets, and the trailing '\0'.
>
>Ideally, we should replace the unsafe sprintf() with snprintf() in
>all users. (>> TODO ;-)

Yeah, good catch. The sizeof() counts the format specifiers too  which never
end up in the output since their expansions are already covered by the other
terms.

I'd rather not poke that bear as part of this series, we can try it in a follow
up and see if anything explodes?

-- 
Thanks,
Sasha
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Randy Dunlap 1 month ago

On 3/3/26 10:21 AM, Sasha Levin wrote:
> diff --git a/Documentation/admin-guide/kallsyms-lineinfo.rst b/Documentation/admin-guide/kallsyms-lineinfo.rst
> new file mode 100644
> index 0000000000000..4dffc18dbcf5a
> --- /dev/null
> +++ b/Documentation/admin-guide/kallsyms-lineinfo.rst
> @@ -0,0 +1,72 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +==================================
> +Kallsyms Source Line Info (LINEINFO)
> +==================================

Heading over/under lines must be at least as long as the heading.

> +
> +Overview
> +========
> +
> +``CONFIG_KALLSYMS_LINEINFO`` embeds DWARF-derived source file and line number
> +mappings into the kernel image so that stack traces include
> +``(file.c:123)`` annotations next to each symbol.  This makes it significantly
> +easier to pinpoint the exact source location during debugging, without needing
> +to manually cross-reference addresses with ``addr2line``.


> diff --git a/MAINTAINERS b/MAINTAINERS
> index 61bf550fd37c2..ab987e74bb0f5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14278,6 +14278,12 @@ F:	lib/Kconfig.kmsan
>  F:	mm/kmsan/
>  F:	scripts/Makefile.kmsan
>  
> +KALLSYMS LINEINFO
> +M:	Sasha Levin <sashal@kernel.org>
> +S:	Maintained
> +F:	Documentation/admin-guide/kallsyms-lineinfo.rst
> +F:	scripts/gen_lineinfo.c

This entry should be in alphabetical order, just before KASAN.

> +
>  KPROBES
>  M:	Naveen N Rao <naveen@kernel.org>
>  M:	"David S. Miller" <davem@davemloft.net>

-- 
~Randy
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Helge Deller 1 month ago
On 3/3/26 19:21, Sasha Levin wrote:
> Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
> lookup table in the kernel image so stack traces directly print source
> file and line number information:
> 
>    root@localhost:~# echo c > /proc/sysrq-trigger
>    [   11.201987] sysrq: Trigger a crash
>    [   11.202831] Kernel panic - not syncing: sysrq triggered crash
>    [   11.206218] Call Trace:
>    [   11.206501]  <TASK>
>    [   11.206749]  dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:94)
>    [   11.207403]  vpanic+0x36e/0x620 (kernel/panic.c:650)
>    [   11.208565]  ? __lock_acquire+0x465/0x2240 (kernel/locking/lockdep.c:4674)
>    [   11.209324]  panic+0xc9/0xd0 (kernel/panic.c:787)
>    [   11.211873]  ? find_held_lock+0x2b/0x80 (kernel/locking/lockdep.c:5350)
>    [   11.212597]  ? lock_release+0xd3/0x300 (kernel/locking/lockdep.c:5535)
>    [   11.213312]  sysrq_handle_crash+0x1a/0x20 (drivers/tty/sysrq.c:154)
>    [   11.214005]  __handle_sysrq.cold+0x66/0x256 (drivers/tty/sysrq.c:611)
>    [   11.214712]  write_sysrq_trigger+0x65/0x80 (drivers/tty/sysrq.c:1221)
>    [   11.215424]  proc_reg_write+0x1bd/0x3c0 (fs/proc/inode.c:330)
>    [   11.216061]  vfs_write+0x1c6/0xff0 (fs/read_write.c:686)
>    [   11.218848]  ksys_write+0xfa/0x200 (fs/read_write.c:740)
>    [   11.222394]  do_syscall_64+0xf3/0x690 (arch/x86/entry/syscall_64.c:63)
>    [   11.223942]  entry_SYSCALL_64_after_hwframe+0x77/0x7f (arch/x86/entry/entry_64.S:121)

As mentioned in the other series, I really like this patch series.

I tested this series again on the parisc architecture, and the relative
directories are now stripped with this version of your patch.
IIRC, the previous patch did show the subdirectory names.
[  132.840382] Backtrace:
[  132.840382]  [<104254d8>] show_stack+0x50/0x64 (traps.c:212)
[  132.840382]  [<1041c0c8>] dump_stack_lvl+0x6c/0xa0 (dump_stack.c:122)
[  132.840382]  [<1041c118>] dump_stack+0x1c/0x2c (dump_stack.c:130)
[  132.840382]  [<10402218>] vpanic+0x154/0x344 (panic.c:550)
[  132.840382]  [<10402438>] panic+0x30/0x34 (panic.c:787)
[  132.840382]  [<10bebea8>] sysrq_handle_crash+0x30/0x34 (rcupdate.h:110)
[  132.840382]  [<10bec720>] __handle_sysrq+0xc0/0x1e4 (preempt.h:14)

Helge
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Sasha Levin 1 month ago
On Wed, Mar 04, 2026 at 09:17:37PM +0100, Helge Deller wrote:
>On 3/3/26 19:21, Sasha Levin wrote:
>>Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
>>lookup table in the kernel image so stack traces directly print source
>>file and line number information:
>>
>>   root@localhost:~# echo c > /proc/sysrq-trigger
>>   [   11.201987] sysrq: Trigger a crash
>>   [   11.202831] Kernel panic - not syncing: sysrq triggered crash
>>   [   11.206218] Call Trace:
>>   [   11.206501]  <TASK>
>>   [   11.206749]  dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:94)
>>   [   11.207403]  vpanic+0x36e/0x620 (kernel/panic.c:650)
>>   [   11.208565]  ? __lock_acquire+0x465/0x2240 (kernel/locking/lockdep.c:4674)
>>   [   11.209324]  panic+0xc9/0xd0 (kernel/panic.c:787)
>>   [   11.211873]  ? find_held_lock+0x2b/0x80 (kernel/locking/lockdep.c:5350)
>>   [   11.212597]  ? lock_release+0xd3/0x300 (kernel/locking/lockdep.c:5535)
>>   [   11.213312]  sysrq_handle_crash+0x1a/0x20 (drivers/tty/sysrq.c:154)
>>   [   11.214005]  __handle_sysrq.cold+0x66/0x256 (drivers/tty/sysrq.c:611)
>>   [   11.214712]  write_sysrq_trigger+0x65/0x80 (drivers/tty/sysrq.c:1221)
>>   [   11.215424]  proc_reg_write+0x1bd/0x3c0 (fs/proc/inode.c:330)
>>   [   11.216061]  vfs_write+0x1c6/0xff0 (fs/read_write.c:686)
>>   [   11.218848]  ksys_write+0xfa/0x200 (fs/read_write.c:740)
>>   [   11.222394]  do_syscall_64+0xf3/0x690 (arch/x86/entry/syscall_64.c:63)
>>   [   11.223942]  entry_SYSCALL_64_after_hwframe+0x77/0x7f (arch/x86/entry/entry_64.S:121)
>
>As mentioned in the other series, I really like this patch series.
>
>I tested this series again on the parisc architecture, and the relative
>directories are now stripped with this version of your patch.
>IIRC, the previous patch did show the subdirectory names.
>[  132.840382] Backtrace:
>[  132.840382]  [<104254d8>] show_stack+0x50/0x64 (traps.c:212)
>[  132.840382]  [<1041c0c8>] dump_stack_lvl+0x6c/0xa0 (dump_stack.c:122)
>[  132.840382]  [<1041c118>] dump_stack+0x1c/0x2c (dump_stack.c:130)
>[  132.840382]  [<10402218>] vpanic+0x154/0x344 (panic.c:550)
>[  132.840382]  [<10402438>] panic+0x30/0x34 (panic.c:787)
>[  132.840382]  [<10bebea8>] sysrq_handle_crash+0x30/0x34 (rcupdate.h:110)
>[  132.840382]  [<10bec720>] __handle_sysrq+0xc0/0x1e4 (preempt.h:14)

Ugh... Can you confirm that you've build this kernel with O=?

The RFC had a dirty dirty hack around how we turn these absolute paths into
relative ones, but I tried to re-do it so no one would yell at me :)

-- 
Thanks,
Sasha
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Helge Deller 1 month ago
On 3/5/26 03:18, Sasha Levin wrote:
> On Wed, Mar 04, 2026 at 09:17:37PM +0100, Helge Deller wrote:
>> On 3/3/26 19:21, Sasha Levin wrote:
>>> Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
>>> lookup table in the kernel image so stack traces directly print source
>>> file and line number information:
>>>
>>>   root@localhost:~# echo c > /proc/sysrq-trigger
>>>   [   11.201987] sysrq: Trigger a crash
>>>   [   11.202831] Kernel panic - not syncing: sysrq triggered crash
>>>   [   11.206218] Call Trace:
>>>   [   11.206501]  <TASK>
>>>   [   11.206749]  dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:94)
>>>   [   11.207403]  vpanic+0x36e/0x620 (kernel/panic.c:650)
>>>   [   11.208565]  ? __lock_acquire+0x465/0x2240 (kernel/locking/lockdep.c:4674)
>>>   [   11.209324]  panic+0xc9/0xd0 (kernel/panic.c:787)
>>>   [   11.211873]  ? find_held_lock+0x2b/0x80 (kernel/locking/lockdep.c:5350)
>>>   [   11.212597]  ? lock_release+0xd3/0x300 (kernel/locking/lockdep.c:5535)
>>>   [   11.213312]  sysrq_handle_crash+0x1a/0x20 (drivers/tty/sysrq.c:154)
>>>   [   11.214005]  __handle_sysrq.cold+0x66/0x256 (drivers/tty/sysrq.c:611)
>>>   [   11.214712]  write_sysrq_trigger+0x65/0x80 (drivers/tty/sysrq.c:1221)
>>>   [   11.215424]  proc_reg_write+0x1bd/0x3c0 (fs/proc/inode.c:330)
>>>   [   11.216061]  vfs_write+0x1c6/0xff0 (fs/read_write.c:686)
>>>   [   11.218848]  ksys_write+0xfa/0x200 (fs/read_write.c:740)
>>>   [   11.222394]  do_syscall_64+0xf3/0x690 (arch/x86/entry/syscall_64.c:63)
>>>   [   11.223942]  entry_SYSCALL_64_after_hwframe+0x77/0x7f (arch/x86/entry/entry_64.S:121)
>>
>> As mentioned in the other series, I really like this patch series.
>>
>> I tested this series again on the parisc architecture, and the relative
>> directories are now stripped with this version of your patch.
>> IIRC, the previous patch did show the subdirectory names.
>> [  132.840382] Backtrace:
>> [  132.840382]  [<104254d8>] show_stack+0x50/0x64 (traps.c:212)
>> [  132.840382]  [<1041c0c8>] dump_stack_lvl+0x6c/0xa0 (dump_stack.c:122)
>> [  132.840382]  [<1041c118>] dump_stack+0x1c/0x2c (dump_stack.c:130)
>> [  132.840382]  [<10402218>] vpanic+0x154/0x344 (panic.c:550)
>> [  132.840382]  [<10402438>] panic+0x30/0x34 (panic.c:787)
>> [  132.840382]  [<10bebea8>] sysrq_handle_crash+0x30/0x34 (rcupdate.h:110)
>> [  132.840382]  [<10bec720>] __handle_sysrq+0xc0/0x1e4 (preempt.h:14)
> 
> Ugh... Can you confirm that you've build this kernel with O=?

Yes. Both -Os and -O2 do not show the relative path.
  
> The RFC had a dirty dirty hack around how we turn these absolute paths into
> relative ones, but I tried to re-do it so no one would yell at me :)

Seems it is needed...

Helge
Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Randy Dunlap 1 month ago

On 3/5/26 2:26 PM, Helge Deller wrote:
> On 3/5/26 03:18, Sasha Levin wrote:
>> On Wed, Mar 04, 2026 at 09:17:37PM +0100, Helge Deller wrote:
>>> On 3/3/26 19:21, Sasha Levin wrote:
>>>> Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
>>>> lookup table in the kernel image so stack traces directly print source
>>>> file and line number information:
>>>>
>>>>   root@localhost:~# echo c > /proc/sysrq-trigger
>>>>   [   11.201987] sysrq: Trigger a crash
>>>>   [   11.202831] Kernel panic - not syncing: sysrq triggered crash
>>>>   [   11.206218] Call Trace:
>>>>   [   11.206501]  <TASK>
>>>>   [   11.206749]  dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:94)
>>>>   [   11.207403]  vpanic+0x36e/0x620 (kernel/panic.c:650)
>>>>   [   11.208565]  ? __lock_acquire+0x465/0x2240 (kernel/locking/lockdep.c:4674)
>>>>   [   11.209324]  panic+0xc9/0xd0 (kernel/panic.c:787)
>>>>   [   11.211873]  ? find_held_lock+0x2b/0x80 (kernel/locking/lockdep.c:5350)
>>>>   [   11.212597]  ? lock_release+0xd3/0x300 (kernel/locking/lockdep.c:5535)
>>>>   [   11.213312]  sysrq_handle_crash+0x1a/0x20 (drivers/tty/sysrq.c:154)
>>>>   [   11.214005]  __handle_sysrq.cold+0x66/0x256 (drivers/tty/sysrq.c:611)
>>>>   [   11.214712]  write_sysrq_trigger+0x65/0x80 (drivers/tty/sysrq.c:1221)
>>>>   [   11.215424]  proc_reg_write+0x1bd/0x3c0 (fs/proc/inode.c:330)
>>>>   [   11.216061]  vfs_write+0x1c6/0xff0 (fs/read_write.c:686)
>>>>   [   11.218848]  ksys_write+0xfa/0x200 (fs/read_write.c:740)
>>>>   [   11.222394]  do_syscall_64+0xf3/0x690 (arch/x86/entry/syscall_64.c:63)
>>>>   [   11.223942]  entry_SYSCALL_64_after_hwframe+0x77/0x7f (arch/x86/entry/entry_64.S:121)
>>>
>>> As mentioned in the other series, I really like this patch series.
>>>
>>> I tested this series again on the parisc architecture, and the relative
>>> directories are now stripped with this version of your patch.
>>> IIRC, the previous patch did show the subdirectory names.
>>> [  132.840382] Backtrace:
>>> [  132.840382]  [<104254d8>] show_stack+0x50/0x64 (traps.c:212)
>>> [  132.840382]  [<1041c0c8>] dump_stack_lvl+0x6c/0xa0 (dump_stack.c:122)
>>> [  132.840382]  [<1041c118>] dump_stack+0x1c/0x2c (dump_stack.c:130)
>>> [  132.840382]  [<10402218>] vpanic+0x154/0x344 (panic.c:550)
>>> [  132.840382]  [<10402438>] panic+0x30/0x34 (panic.c:787)
>>> [  132.840382]  [<10bebea8>] sysrq_handle_crash+0x30/0x34 (rcupdate.h:110)
>>> [  132.840382]  [<10bec720>] __handle_sysrq+0xc0/0x1e4 (preempt.h:14)
>>
>> Ugh... Can you confirm that you've build this kernel with O=?
> 
> Yes. Both -Os and -O2 do not show the relative path.

Helge,
I'm fairly sure that Sasha meant with O=build_dir_name,
not -O for optimization levels.

>> The RFC had a dirty dirty hack around how we turn these absolute paths into
>> relative ones, but I tried to re-do it so no one would yell at me :)
> 
> Seems it is needed...
> 
> Helge
> 

-- 
~Randy

Re: [PATCH 1/3] kallsyms: embed source file:line info in kernel stack traces
Posted by Helge Deller 1 month ago
On 3/6/26 06:31, Randy Dunlap wrote:
> On 3/5/26 2:26 PM, Helge Deller wrote:
>> On 3/5/26 03:18, Sasha Levin wrote:
>>> On Wed, Mar 04, 2026 at 09:17:37PM +0100, Helge Deller wrote:
>>>> On 3/3/26 19:21, Sasha Levin wrote:
>>>>> Add CONFIG_KALLSYMS_LINEINFO, which embeds a compact address-to-line
>>>>> lookup table in the kernel image so stack traces directly print source
>>>>> file and line number information:
>>>>>
>>>>>    root@localhost:~# echo c > /proc/sysrq-trigger
>>>>>    [   11.201987] sysrq: Trigger a crash
>>>>>    [   11.202831] Kernel panic - not syncing: sysrq triggered crash
>>>>>    [   11.206218] Call Trace:
>>>>>    [   11.206501]  <TASK>
>>>>>    [   11.206749]  dump_stack_lvl+0x5d/0x80 (lib/dump_stack.c:94)
>>>>>    [   11.207403]  vpanic+0x36e/0x620 (kernel/panic.c:650)
>>>>>    [   11.208565]  ? __lock_acquire+0x465/0x2240 (kernel/locking/lockdep.c:4674)
>>>>>    [   11.209324]  panic+0xc9/0xd0 (kernel/panic.c:787)
>>>>>    [   11.211873]  ? find_held_lock+0x2b/0x80 (kernel/locking/lockdep.c:5350)
>>>>>    [   11.212597]  ? lock_release+0xd3/0x300 (kernel/locking/lockdep.c:5535)
>>>>>    [   11.213312]  sysrq_handle_crash+0x1a/0x20 (drivers/tty/sysrq.c:154)
>>>>>    [   11.214005]  __handle_sysrq.cold+0x66/0x256 (drivers/tty/sysrq.c:611)
>>>>>    [   11.214712]  write_sysrq_trigger+0x65/0x80 (drivers/tty/sysrq.c:1221)
>>>>>    [   11.215424]  proc_reg_write+0x1bd/0x3c0 (fs/proc/inode.c:330)
>>>>>    [   11.216061]  vfs_write+0x1c6/0xff0 (fs/read_write.c:686)
>>>>>    [   11.218848]  ksys_write+0xfa/0x200 (fs/read_write.c:740)
>>>>>    [   11.222394]  do_syscall_64+0xf3/0x690 (arch/x86/entry/syscall_64.c:63)
>>>>>    [   11.223942]  entry_SYSCALL_64_after_hwframe+0x77/0x7f (arch/x86/entry/entry_64.S:121)
>>>>
>>>> As mentioned in the other series, I really like this patch series.
>>>>
>>>> I tested this series again on the parisc architecture, and the relative
>>>> directories are now stripped with this version of your patch.
>>>> IIRC, the previous patch did show the subdirectory names.
>>>> [  132.840382] Backtrace:
>>>> [  132.840382]  [<104254d8>] show_stack+0x50/0x64 (traps.c:212)
>>>> [  132.840382]  [<1041c0c8>] dump_stack_lvl+0x6c/0xa0 (dump_stack.c:122)
>>>> [  132.840382]  [<1041c118>] dump_stack+0x1c/0x2c (dump_stack.c:130)
>>>> [  132.840382]  [<10402218>] vpanic+0x154/0x344 (panic.c:550)
>>>> [  132.840382]  [<10402438>] panic+0x30/0x34 (panic.c:787)
>>>> [  132.840382]  [<10bebea8>] sysrq_handle_crash+0x30/0x34 (rcupdate.h:110)
>>>> [  132.840382]  [<10bec720>] __handle_sysrq+0xc0/0x1e4 (preempt.h:14)
>>>
>>> Ugh... Can you confirm that you've build this kernel with O=?
>>
>> Yes. Both -Os and -O2 do not show the relative path.
> 
> Helge,
> I'm fairly sure that Sasha meant with O=build_dir_name,
> not -O for optimization levels.

Ah, ok.
Anyway, I checked again.
I did *not* used a "O=..." parameter in either case.
I recompiled again with the RFC patch, and here the relative paths show up again (even without O=):
  
  root@debian:~# echo c > /proc/sysrq-trigger
[  121.172011] sysrq: Trigger a crash
[  121.173986] Kernel panic - not syncing: sysrq triggered crash
[  121.176141] CPU: 1 UID: 0 PID: 382 Comm: bash Not tainted 7.0.0-rc2-32bit+ #2971 VOLUNTARY
[  121.177041] Hardware name: 9000/778/B160L
[  121.178092] Backtrace:
[  121.178533]  [<104254d8>] show_stack+0x50/0x64 (kernel/traps.c:212)
[  121.181408]  [<1041c0c8>] dump_stack_lvl+0x6c/0xa0 (lib/dump_stack.c:122)
[  121.182145]  [<1041c118>] dump_stack+0x1c/0x2c (lib/dump_stack.c:130)
[  121.182779]  [<10402218>] vpanic+0x154/0x344 (kernel/panic.c:552)
[  121.182871]  [<10402438>] panic+0x30/0x34 (kernel/panic.c:787)
[  121.182871]  [<10beb5a0>] sysrq_handle_crash+0x30/0x34 (drivers/tty/sysrq.c:154)
[  121.182871]  [<10bebe18>] __handle_sysrq+0xc0/0x1e4 (arch/parisc/include/asm/current.h:13)
[  121.182871]  [<10bec7c0>] write_sysrq_trigger+0x8c/0xcc (drivers/tty/sysrq.c:1223)
[  121.182871]  [<106ba460>] proc_reg_write+0xd0/0x10c (fs/proc/inode.c:343)
[  121.182871]  [<1060faf0>] vfs_write+0xb8/0x46c (fs/read_write.c:691)
[  121.182871]  [<1061005c>] ksys_write+0x78/0x118 (fs/read_write.c:741)
[  121.182871]  [<10610114>] sys_write+0x18/0x28 (fs/read_write.c:748)
[  121.182871]  [<10421334>] syscall_exit+0x0/0x10 (kernel/entry.S:1722)

Helge
  
>>> The RFC had a dirty dirty hack around how we turn these absolute paths into
>>> relative ones, but I tried to re-do it so no one would yell at me :)
>>
>> Seems it is needed...