Live Patching
 help / color / mirror / Atom feed
* [PATCH 7.2 0974/1815] objtool/klp: Fix module name normalization for paths with dots
       [not found] <20260912065648.999753832@linuxfoundation.org>
@ 2026-09-12  6:45 ` Greg Kroah-Hartman
  2026-09-12  6:45 ` [PATCH 7.2 0975/1815] objtool/klp: Normalize Module.symvers paths to module names Greg Kroah-Hartman
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 5+ messages in thread
From: Greg Kroah-Hartman @ 2026-09-12  6:45 UTC (permalink / raw)
  To: stable
  Cc: Greg Kroah-Hartman, patches, Sashiko, Josh Poimboeuf, Ingo Molnar,
	live-patching, Sasha Levin

7.2-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Josh Poimboeuf <jpoimboe@kernel.org>

[ Upstream commit 165affd6f095323d953b0ed5823ec4d0db3b7d0d ]

When .modinfo has no "name=" tag, __find_modname() falls back to
converting the object's build-tree path to a runtime module name by
stripping directory components, converting '-' to '_' and truncating the
file extension.

It does all that in a single pass over the entire path, so the first dot
anywhere in the path ends the name.  For an object built in a directory
whose name contains a dot, e.g. "drivers/foo-1.0/bar.o", the result is a
bogus module name.

Strip the directory components up front so only the basename is scanned
for the extension separator.

Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: live-patching@vger.kernel.org
Link: https://patch.msgid.link/9017b4609553bed16674e8f924d34691cbc2b2c1.1785727106.git.jpoimboe@kernel.org
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 tools/objtool/klp-diff.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index f8787d7d14547..aeb99d572300c 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1140,7 +1140,7 @@ static struct export *find_export(struct symbol *sym)
 static const char *__find_modname(struct elfs *e)
 {
 	struct section *sec;
-	char *name;
+	char *name, *slash;
 
 	sec = find_section_by_name(e->orig, ".modinfo");
 	if (!sec) {
@@ -1158,10 +1158,12 @@ static const char *__find_modname(struct elfs *e)
 		return NULL;
 	}
 
+	slash = strrchr(name, '/');
+	if (slash)
+		name = slash + 1;
+
 	for (char *c = name; *c; c++) {
-		if (*c == '/')
-			name = c + 1;
-		else if (*c == '-')
+		if (*c == '-')
 			*c = '_';
 		else if (*c == '.') {
 			*c = '\0';
-- 
2.53.0




^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 7.2 0975/1815] objtool/klp: Normalize Module.symvers paths to module names
       [not found] <20260912065648.999753832@linuxfoundation.org>
  2026-09-12  6:45 ` [PATCH 7.2 0974/1815] objtool/klp: Fix module name normalization for paths with dots Greg Kroah-Hartman
@ 2026-09-12  6:45 ` Greg Kroah-Hartman
  2026-09-12  6:45 ` [PATCH 7.2 0976/1815] objtool/klp: Fix false module dependencies caused by dead relocs Greg Kroah-Hartman
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 5+ messages in thread
From: Greg Kroah-Hartman @ 2026-09-12  6:45 UTC (permalink / raw)
  To: stable
  Cc: Greg Kroah-Hartman, patches, Ben Procknow, Joe Lawrence,
	Josh Poimboeuf, Ingo Molnar, Miroslav Benes, live-patching,
	Sasha Levin

7.2-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Joe Lawrence <joe.lawrence@redhat.com>

[ Upstream commit 8668bf91e0508abeb8e98b4edd89032be02228a1 ]

Module.symvers contains build-tree object paths as module identifiers
(e.g., "arch/x86/kvm/kvm") rather than runtime module names ("kvm").
Objtool's clone_reloc_klp() uses this field directly for exported
symbols, while unexported symbols correctly go through __find_modname().

This means that exported symbol relocations may land in a .klp.rela
section named with the build path rather than the module name.  That is
a crash waiting to happen: the kernel's livepatch loader silently skips
this relocation because it doesn't match the expected klp_object name.
The unresolved relocation sits in the newly activated code, crashing
when executed.

Normalize export->mod at Module.symvers read time using the same logic
as __find_modname() (refactored into a shared normalize_modname()
helper).

Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Reported-by: Ben Procknow <bprockno@redhat.com>
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Cc: live-patching@vger.kernel.org
Link: https://patch.msgid.link/dbe1b72931bd3c31b751fd0729613d9f2226fff6.1785727106.git.jpoimboe@kernel.org
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 tools/objtool/klp-diff.c | 49 ++++++++++++++++++++++++++++------------
 1 file changed, 34 insertions(+), 15 deletions(-)

diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index aeb99d572300c..15d37d955af0e 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -83,6 +83,35 @@ static char *escape_str(const char *orig)
 	return new;
 }
 
+/*
+ * Convert a build-tree object path to a runtime module name: strip
+ * directory components, replace '-' with '_', and remove file
+ * extensions.  Examples:
+ *
+ *   "arch/x86/kvm/kvm" -> "kvm"
+ *   "arch/x86/kvm/kvm-intel" -> "kvm_intel".
+ *
+ * Used by read_exports() to normalize Module.symvers entries and by
+ * __find_modname() as a fallback when .modinfo lacks a "name=" tag.
+ */
+static char *normalize_modname(char *name)
+{
+	char *slash = strrchr(name, '/');
+
+	if (slash)
+		name = slash + 1;
+
+	for (char *c = name; *c; c++) {
+		if (*c == '-')
+			*c = '_';
+		else if (*c == '.') {
+			*c = '\0';
+			break;
+		}
+	}
+	return name;
+}
+
 static int read_exports(void)
 {
 	const char *symvers = "Module.symvers";
@@ -150,6 +179,9 @@ static int read_exports(void)
 			return -1;
 		}
 
+		if (strcmp(export->mod, "vmlinux"))
+			export->mod = normalize_modname(export->mod);
+
 		export->sym = strdup(sym);
 		if (!export->sym) {
 			ERROR_GLIBC("strdup");
@@ -1140,7 +1172,7 @@ static struct export *find_export(struct symbol *sym)
 static const char *__find_modname(struct elfs *e)
 {
 	struct section *sec;
-	char *name, *slash;
+	char *name;
 
 	sec = find_section_by_name(e->orig, ".modinfo");
 	if (!sec) {
@@ -1158,20 +1190,7 @@ static const char *__find_modname(struct elfs *e)
 		return NULL;
 	}
 
-	slash = strrchr(name, '/');
-	if (slash)
-		name = slash + 1;
-
-	for (char *c = name; *c; c++) {
-		if (*c == '-')
-			*c = '_';
-		else if (*c == '.') {
-			*c = '\0';
-			break;
-		}
-	}
-
-	return name;
+	return normalize_modname(name);
 }
 
 /* Get the object's module name as defined by the kernel (and klp_object) */
-- 
2.53.0




^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 7.2 0976/1815] objtool/klp: Fix false module dependencies caused by dead relocs
       [not found] <20260912065648.999753832@linuxfoundation.org>
  2026-09-12  6:45 ` [PATCH 7.2 0974/1815] objtool/klp: Fix module name normalization for paths with dots Greg Kroah-Hartman
  2026-09-12  6:45 ` [PATCH 7.2 0975/1815] objtool/klp: Normalize Module.symvers paths to module names Greg Kroah-Hartman
@ 2026-09-12  6:45 ` Greg Kroah-Hartman
  2026-09-12  6:45 ` [PATCH 7.2 0977/1815] objtool/klp: Add .klp.symid for sympos disambiguation Greg Kroah-Hartman
  2026-09-12  6:45 ` [PATCH 7.2 0978/1815] objtool/klp: Fix symbol resolution for duplicate data symbols Greg Kroah-Hartman
  4 siblings, 0 replies; 5+ messages in thread
From: Greg Kroah-Hartman @ 2026-09-12  6:45 UTC (permalink / raw)
  To: stable
  Cc: Greg Kroah-Hartman, patches, Ben Procknow, Joe Lawrence,
	Josh Poimboeuf, Ingo Molnar, live-patching, Sasha Levin

7.2-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Josh Poimboeuf <jpoimboe@kernel.org>

[ Upstream commit 5ca8c91d1ea6534842e7e0065d15104d802506cd ]

When creating a klp reloc, klp-diff keeps the original relocation but
converts the referenced symbol to an UNDEF/WEAK placeholder tombstone
symbol, which gets fully disabled later by klp post-link.  The tombstone
symbol is only needed to avoid confusing objtool when it does the final
run on the patch module.

However, for references to exported symbols, modpost sees the reference
to the tombstone symbol as a real reference to an exported symbol,
resulting in a false module dependency getting created.

Further, for a reference to a tombstone symbol which is exported into a
module namespace, e.g. via EXPORT_SYMBOL_FOR_KVM_INTERNAL(), modpost
can't satisfy the dependency, resulting in a warning like the following:

  module ... uses symbol kvm_flush_remote_tlbs from namespace
  module:kvm-amd,kvm-intel, but does not import it.

Rename the placeholder tombstone symbols to ".klp.tombstone.<name>" so
modpost no longer recognizes them.

Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Reported-by: Ben Procknow <bprockno@redhat.com>
Reported-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: live-patching@vger.kernel.org
Link: https://lore.kernel.org/20260720145658.1103243-5-joe.lawrence@redhat.com
Link: https://patch.msgid.link/9548393f4d89ec3b498f4f69aa6ef6b9bb7150fe.1785727106.git.jpoimboe@kernel.org
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 tools/objtool/elf.c                 | 13 +++++++++++++
 tools/objtool/include/objtool/klp.h |  2 ++
 tools/objtool/klp-diff.c            | 16 ++++++++++++----
 3 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 33c95a74a51bd..a791f4ea6ec19 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -23,6 +23,7 @@
 #include <linux/log2.h>
 #include <objtool/builtin.h>
 #include <objtool/elf.h>
+#include <objtool/klp.h>
 #include <objtool/warn.h>
 
 static ssize_t demangled_name_len(const char *name);
@@ -626,6 +627,18 @@ static int read_symbols(struct elf *elf)
 			return -1;
 		}
 
+		/*
+		 * "klp diff" renames the placeholder symbols of KLP relocs to
+		 * hide them from modpost.  Hide the prefix from the rest of
+		 * objtool so its many name-based heuristics (noreturns,
+		 * uaccess safe list, ...) still see the original symbol name.
+		 *
+		 * st_name is left alone, so the renamed symbol is preserved in
+		 * the output file.
+		 */
+		if (strstarts(sym->name, KLP_TOMBSTONE_PREFIX))
+			sym->name += strlen(KLP_TOMBSTONE_PREFIX);
+
 		if ((sym->sym.st_shndx > SHN_UNDEF &&
 		     sym->sym.st_shndx < SHN_LORESERVE) ||
 		    (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) {
diff --git a/tools/objtool/include/objtool/klp.h b/tools/objtool/include/objtool/klp.h
index 6f60cf05db864..aab6db42052dd 100644
--- a/tools/objtool/include/objtool/klp.h
+++ b/tools/objtool/include/objtool/klp.h
@@ -23,6 +23,8 @@
 #define KLP_RELOCS_SEC	"__klp_relocs"
 #define KLP_STRINGS_SEC	".rodata.klp.str1.1"
 
+#define KLP_TOMBSTONE_PREFIX	".klp.tombstone."
+
 struct klp_reloc {
 	void *offset;
 	void *sym;
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 15d37d955af0e..75ba0e060a34d 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1362,6 +1362,7 @@ static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc,
 	s64 addend = reloc_addend(patched_reloc);
 	const char *sym_modname, *sym_orig_name;
 	static struct section *klp_relocs;
+	char tombstone_name[SYM_NAME_LEN];
 	struct symbol *sym, *klp_sym;
 	unsigned long klp_reloc_off;
 	char sym_name[SYM_NAME_LEN];
@@ -1376,15 +1377,22 @@ static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc,
 	/*
 	 * Keep the original reloc intact for now to avoid breaking objtool run
 	 * which relies on proper relocations for many of its features.  This
-	 * will be disabled later by "objtool klp post-link".
+	 * reloc now targets a functionally dead tombstone symbol and will be
+	 * disabled later by "objtool klp post-link".
 	 *
-	 * Convert it to UNDEF (and WEAK to avoid modpost warnings).
+	 * Convert the symbol to UNDEF/WEAK and rename to
+	 * .klp.tombstone.sym_name to prevent modpost from printing warnings or
+	 * creating false module dependencies.  The prefix is hidden from the
+	 * objtool run itself by read_symbols().
 	 */
 
 	sym = patched_sym->clone;
 	if (!sym) {
-		/* STB_WEAK: avoid modpost undefined symbol warnings */
-		sym = elf_create_symbol(e->out, patched_sym->name, NULL,
+		if (snprintf_check(tombstone_name, SYM_NAME_LEN,
+				   KLP_TOMBSTONE_PREFIX "%s", patched_sym->name))
+			return -1;
+
+		sym = elf_create_symbol(e->out, tombstone_name, NULL,
 					STB_WEAK, patched_sym->type, 0, 0);
 		if (!sym)
 			return -1;
-- 
2.53.0




^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 7.2 0977/1815] objtool/klp: Add .klp.symid for sympos disambiguation
       [not found] <20260912065648.999753832@linuxfoundation.org>
                   ` (2 preceding siblings ...)
  2026-09-12  6:45 ` [PATCH 7.2 0976/1815] objtool/klp: Fix false module dependencies caused by dead relocs Greg Kroah-Hartman
@ 2026-09-12  6:45 ` Greg Kroah-Hartman
  2026-09-12  6:45 ` [PATCH 7.2 0978/1815] objtool/klp: Fix symbol resolution for duplicate data symbols Greg Kroah-Hartman
  4 siblings, 0 replies; 5+ messages in thread
From: Greg Kroah-Hartman @ 2026-09-12  6:45 UTC (permalink / raw)
  To: stable
  Cc: Greg Kroah-Hartman, patches, Josh Poimboeuf, Ingo Molnar,
	live-patching, Sasha Levin

7.2-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Josh Poimboeuf <jpoimboe@kernel.org>

[ Upstream commit 029223d301620bc4e1086696047b0d5d6eba5edd ]

Livepatch identifies a duplicate-named symbol by its position (sympos)
among same-named kallsyms entries, which for vmlinux are counted in
ascending address order in the final linked kernel.  That order can't be
reliably derived from vmlinux.o: the final link reorders sub-sections
(.text.unlikely*, .data..*, etc).

Bridge the gap with a new .klp.symid section which can be used to
correlate symbols between vmlinux.o and vmlinux so that klp-diff can
reliably determine the sympos.

The table can't survive --gc-sections: keeping it alive would keep every
duplicate-named symbol's section alive, so the reference kernel would
stop matching the one which ships.  klp-build rejects
CONFIG_LD_DEAD_CODE_DATA_ELIMINATION instead.  Nothing is lost today:
x86_64 is the only HAVE_KLP_BUILD arch and doesn't select
HAVE_LD_DEAD_CODE_DATA_ELIMINATION, arm64 and s390 have never selected
it either, and on powerpc, it's still EXPERIMENTAL and disabled by every
distro kernel.

This is the build-time half of reliable vmlinux sympos computation;
"objtool klp diff" will consume the table in a subsequent commit.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: live-patching@vger.kernel.org
Link: https://patch.msgid.link/64d50f077b569f47883c015cdb7079edb068efe8.1785727106.git.jpoimboe@kernel.org
Stable-dep-of: 15fa203ef91e ("objtool/klp: Fix symbol resolution for duplicate data symbols")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 include/asm-generic/vmlinux.lds.h       |  10 +-
 scripts/Makefile.vmlinux_o              |   3 +
 scripts/livepatch/klp-build             |   5 +
 scripts/mod/modpost.c                   |   1 +
 tools/objtool/Build                     |   1 +
 tools/objtool/builtin-check.c           |   7 ++
 tools/objtool/check.c                   |   7 ++
 tools/objtool/include/objtool/builtin.h |   1 +
 tools/objtool/include/objtool/klp.h     |  15 +++
 tools/objtool/klp-symid.c               | 117 ++++++++++++++++++++++++
 10 files changed, 166 insertions(+), 1 deletion(-)
 create mode 100644 tools/objtool/klp-symid.c

diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 5659f4b5a1252..ee9c5d354a856 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -839,12 +839,20 @@
 		.stab.index 0 : { *(.stab.index) }			\
 		.stab.indexstr 0 : { *(.stab.indexstr) }
 
+#ifdef CONFIG_KLP_BUILD
+#define KLP_SYMID							\
+		.klp.symid 0 : { *(.klp.symid) }
+#else
+#define KLP_SYMID
+#endif
+
 /* Required sections not related to debugging. */
 #define ELF_DETAILS							\
 		.comment 0 : { *(.comment) }				\
 		.symtab 0 : { *(.symtab) }				\
 		.strtab 0 : { *(.strtab) }				\
-		.shstrtab 0 : { *(.shstrtab) }
+		.shstrtab 0 : { *(.shstrtab) }				\
+		KLP_SYMID
 
 #define MODINFO								\
 		.modinfo : { *(.modinfo) . = ALIGN(8); }
diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
index 527352c222ff6..24a3a4fd271c2 100644
--- a/scripts/Makefile.vmlinux_o
+++ b/scripts/Makefile.vmlinux_o
@@ -47,6 +47,9 @@ endif
 vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION)	+= --noinstr \
 							   $(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)
 
+# Only used for builds initiated by klp-build
+vmlinux-objtool-args-$(if $(KLP_SYMIDS),y)		+= --klp-symids
+
 objtool-args = $(vmlinux-objtool-args-y) --link
 
 # Link of vmlinux.o used for section mismatch analysis
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index c4a7acf8edc3f..d2b12fb68740b 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -271,6 +271,9 @@ validate_config() {
 	[[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] &&	\
 		die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported"
 
+	[[ -v CONFIG_LD_DEAD_CODE_DATA_ELIMINATION ]] &&		\
+		die "kernel option 'CONFIG_LD_DEAD_CODE_DATA_ELIMINATION' not supported"
+
 	[[ -v CONFIG_AS_IS_LLVM ]] &&				\
 		[[ "$CONFIG_AS_VERSION" -lt 200000 ]] &&	\
 		die "Clang assembler version < 20 not supported"
@@ -555,6 +558,8 @@ build_kernel() {
 	#
 	cmd+=("KBUILD_MODPOST_WARN=1")
 
+	cmd+=("KLP_SYMIDS=1")
+
 	if [[ -v VERBOSE ]]; then
 		cmd+=("V=1")
 	else
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index a7b72a81d2482..027944fe35b47 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -767,6 +767,7 @@ static const char *const section_white_list[] =
 	".llvm.call-graph-profile",	/* call graph */
 	"__llvm_covfun",
 	"__llvm_covmap",
+	".klp.symid",			/* objtool --klp-symids */
 	NULL
 };
 
diff --git a/tools/objtool/Build b/tools/objtool/Build
index 93a37b0dfd313..506f89bed808e 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -6,6 +6,7 @@ objtool-y += check.o
 objtool-y += special.o
 objtool-y += builtin-check.o
 objtool-y += elf.o
+objtool-y += klp-symid.o
 objtool-y += objtool.o
 
 objtool-$(BUILD_DISAS) += disas.o
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 118c3de2f293e..75b11dc85010e 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -76,6 +76,7 @@ static const struct option check_options[] = {
 	OPT_STRING_OPTARG('d',	 "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
 	OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
 	OPT_BOOLEAN('i',	 "ibt", &opts.ibt, "validate and annotate IBT"),
+	OPT_BOOLEAN(0,		 "klp-symids", &opts.klp_symids, "generate .klp.symids for duplicate symbol disambiguation"),
 	OPT_BOOLEAN('m',	 "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
 	OPT_BOOLEAN(0,		 "noabs", &opts.noabs, "reject absolute references in allocatable sections"),
 	OPT_BOOLEAN('n',	 "noinstr", &opts.noinstr, "validate noinstr rules"),
@@ -174,10 +175,16 @@ static bool opts_valid(void)
 		return false;
 	}
 
+	if (opts.klp_symids && !opts.link) {
+		ERROR("--klp-symids requires --link");
+		return false;
+	}
+
 	if (opts.disas			||
 	    opts.hack_jump_label	||
 	    opts.hack_noinstr		||
 	    opts.ibt			||
+	    opts.klp_symids		||
 	    opts.mcount			||
 	    opts.noabs			||
 	    opts.noinstr		||
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 3ab5b9f1c6a42..28cc2fed6f15e 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -15,6 +15,7 @@
 #include <objtool/arch.h>
 #include <objtool/disas.h>
 #include <objtool/check.h>
+#include <objtool/klp.h>
 #include <objtool/special.h>
 #include <objtool/trace.h>
 #include <objtool/warn.h>
@@ -4925,6 +4926,12 @@ int check(struct objtool_file *file)
 			goto out;
 	}
 
+	if (opts.klp_symids) {
+		ret = klp_create_symid_sections(file);
+		if (ret)
+			goto out;
+	}
+
 	if (opts.noabs)
 		warnings += check_abs_references(file);
 
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index e844e9c82b7b2..349690bb1c50e 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -16,6 +16,7 @@ struct opts {
 	bool hack_noinstr;
 	bool hack_skylake;
 	bool ibt;
+	bool klp_symids;
 	bool mcount;
 	bool noabs;
 	bool noinstr;
diff --git a/tools/objtool/include/objtool/klp.h b/tools/objtool/include/objtool/klp.h
index aab6db42052dd..4d3c3bd462aa5 100644
--- a/tools/objtool/include/objtool/klp.h
+++ b/tools/objtool/include/objtool/klp.h
@@ -31,6 +31,21 @@ struct klp_reloc {
 	u32 type;
 };
 
+/*
+ * .klp.symid is used to correlate symbols between vmlinux.o and vmlinux, for
+ * calculating sympos to disambiguate duplicately-named symbols.
+ */
+#define KLP_SYMID_SEC	".klp.symid"
+
+struct klp_symid {
+	u64 id;
+	u64 addr;
+};
+
+struct objtool_file;
+
+int klp_create_symid_sections(struct objtool_file *file);
+
 int cmd_klp_checksum(int argc, const char **argv);
 int cmd_klp_diff(int argc, const char **argv);
 int cmd_klp_post_link(int argc, const char **argv);
diff --git a/tools/objtool/klp-symid.c b/tools/objtool/klp-symid.c
new file mode 100644
index 0000000000000..cf188cdfa6079
--- /dev/null
+++ b/tools/objtool/klp-symid.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Emit the .klp.symid table which allows "objtool klp diff" to reliably
+ * disambiguate duplicate-named local symbols in vmlinux.
+ *
+ * Livepatch identifies a duplicate-named symbol by its position (sympos)
+ * among the same-named kallsyms entries, counted in ascending address order
+ * in the final linked vmlinux.  That order can't be derived from vmlinux.o
+ * alone: the final link reorders sub-sections (.text.unlikely*, .data..*,
+ * etc).
+ *
+ * Bridge the gap with a table which survives the final link: a single
+ * non-alloc section containing an array of { id, addr } entries, where
+ * 'id' is a unique counter identifier and 'addr' has a relocation to the
+ * symbol.  The linker copies 'id' verbatim and resolves 'addr' to the symbol's
+ * final address.
+ *
+ * The table is only emitted for vmlinux.o, and only when klp-build asks for it
+ * with KLP_SYMIDS=1, which adds --klp-symids to the vmlinux.o objtool run.
+ *
+ * It can't survive --gc-sections, which sweeps the whole section; klp-build
+ * rejects CONFIG_LD_DEAD_CODE_DATA_ELIMINATION.
+ */
+#include <linux/string.h>
+
+#include <objtool/objtool.h>
+#include <objtool/warn.h>
+#include <objtool/endianness.h>
+#include <objtool/klp.h>
+
+static const char * const discarded_secs[] = {
+	".discard",
+	".modinfo",
+	"__tracepoint_check",
+};
+
+static bool discarded_sec(struct section *sec)
+{
+	if (!(sec->sh.sh_flags & SHF_ALLOC))
+		return true;
+
+	for (int i = 0; i < ARRAY_SIZE(discarded_secs); i++)
+		if (strstarts(sec->name, discarded_secs[i]))
+			return true;
+
+	return false;
+}
+
+static bool symid_needed(struct elf *elf, struct symbol *sym)
+{
+	struct symbol *s;
+
+	if (!is_local_sym(sym) || is_undef_sym(sym))
+		return false;
+
+	if (!is_func_sym(sym) && !is_object_sym(sym))
+		return false;
+
+	if (is_prefix_func(sym))
+		return false;
+
+	if (discarded_sec(sym->sec))
+		return false;
+
+	for_each_sym_by_name(elf, sym->name, s) {
+		if (s == sym || is_sec_sym(s) || is_file_sym(s) || is_undef_sym(s))
+			continue;
+		return true;
+	}
+
+	return false;
+}
+
+int klp_create_symid_sections(struct objtool_file *file)
+{
+	struct elf *elf = file->elf;
+	struct klp_symid *symids;
+	struct section *sec;
+	struct symbol *sym;
+	u64 nr = 0, i = 0;
+
+	if (!str_ends_with(objname, "vmlinux.o"))
+		return 0;
+
+	for_each_sym(elf, sym)
+		if (symid_needed(elf, sym))
+			nr++;
+
+	if (!nr)
+		return 0;
+
+	sec = elf_create_section(elf, KLP_SYMID_SEC, 0, sizeof(struct klp_symid),
+				 SHT_PROGBITS, 8, 0);
+	if (!sec)
+		return -1;
+
+	symids = elf_add_data(elf, sec, NULL, nr * sizeof(struct klp_symid));
+	if (!symids)
+		return -1;
+
+	for_each_sym(elf, sym) {
+		if (!symid_needed(elf, sym))
+			continue;
+
+		symids[i].id = bswap_if_needed(elf, i);
+
+		if (!elf_create_reloc(elf, sec,
+				      i * sizeof(struct klp_symid) +
+				      offsetof(struct klp_symid, addr),
+				      sym, 0, R_ABS64))
+			return -1;
+
+		i++;
+	}
+
+	return 0;
+}
-- 
2.53.0




^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 7.2 0978/1815] objtool/klp: Fix symbol resolution for duplicate data symbols
       [not found] <20260912065648.999753832@linuxfoundation.org>
                   ` (3 preceding siblings ...)
  2026-09-12  6:45 ` [PATCH 7.2 0977/1815] objtool/klp: Add .klp.symid for sympos disambiguation Greg Kroah-Hartman
@ 2026-09-12  6:45 ` Greg Kroah-Hartman
  4 siblings, 0 replies; 5+ messages in thread
From: Greg Kroah-Hartman @ 2026-09-12  6:45 UTC (permalink / raw)
  To: stable
  Cc: Greg Kroah-Hartman, patches, Ben Procknow, Joe Lawrence,
	Josh Poimboeuf, Ingo Molnar, live-patching, Sasha Levin

7.2-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Josh Poimboeuf <jpoimboe@kernel.org>

[ Upstream commit 15fa203ef91e8a303c322eaaa8ca01a6ddaf94dc ]

find_sympos() calculates a sympos used by livepatch to disambiguate
duplicately-named symbols.  For function symbols, there's a hack which
counts .text.unlikely symbols before other .text symbols, matching the
linker script's section ordering.

Not only is the hack fragile, data symbols can have the same problem.
So for example, adding a reference to pwq_cache in
ep_unregister_pollwait() can trigger a corrupt sympos and a relocation
to the wrong pwq_cache symbol in the livepatch module, resulting in a
crash or undefined behavior.

Remove the existing hack in favor of a fully deterministic solution,
using the new .klp.symid table to derive the symbol-to-id mapping from
the original vmlinux.o and the id-to-address mapping from the
corresponding vmlinux, which can then be used to determine the exact
sympos associated with the original vmlinux.

Modules don't need any special treatment: the .ko has the same
section/symbol ordering as the original whole-archive symbol table.

Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Reported-by: Ben Procknow <bprockno@redhat.com>
Reported-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: live-patching@vger.kernel.org
Link: https://lore.kernel.org/20260710153042.3156788-1-joe.lawrence@redhat.com
Link: https://lore.kernel.org/20260724221730.3126529-1-joe.lawrence@redhat.com
Link: https://patch.msgid.link/919785e3bf2245db02ff6391e735d9cb139170b1.1785727106.git.jpoimboe@kernel.org
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 scripts/livepatch/klp-build         |   4 +
 tools/objtool/Build                 |   3 +-
 tools/objtool/include/objtool/klp.h |   5 +
 tools/objtool/klp-diff.c            |  66 +----
 tools/objtool/klp-sympos.c          | 411 ++++++++++++++++++++++++++++
 5 files changed, 427 insertions(+), 62 deletions(-)
 create mode 100644 tools/objtool/klp-sympos.c

diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index d2b12fb68740b..30cd881ca7ed1 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -610,6 +610,8 @@ copy_orig_objects() {
 	done
 	xtrace_restore
 
+	cp -f "$PWD/vmlinux" "$ORIG_DIR" || die "missing vmlinux"
+
 	mv -f "$TMP_DIR/build.log" "$ORIG_DIR"
 	touch "$TIMESTAMP"
 	touch "$ORIG_DIR/.complete"
@@ -680,6 +682,8 @@ generate_checksums() {
 		"$OBJTOOL" klp checksum "$dest"
 	done
 
+	[[ -f "$src_dir/vmlinux" ]] && cp -f "$src_dir/vmlinux" "$dest_dir"
+
 	touch "$dest_dir/.complete"
 }
 
diff --git a/tools/objtool/Build b/tools/objtool/Build
index 506f89bed808e..59f9486280981 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -13,7 +13,8 @@ objtool-$(BUILD_DISAS) += disas.o
 objtool-$(BUILD_DISAS) += trace.o
 
 objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
-objtool-$(BUILD_KLP) += builtin-klp.o klp-checksum.o klp-diff.o klp-post-link.o
+objtool-$(BUILD_KLP) += builtin-klp.o klp-checksum.o klp-diff.o \
+			klp-post-link.o klp-sympos.o
 
 objtool-y += libstring.o
 objtool-y += libctype.o
diff --git a/tools/objtool/include/objtool/klp.h b/tools/objtool/include/objtool/klp.h
index 4d3c3bd462aa5..0118c2c170c3f 100644
--- a/tools/objtool/include/objtool/klp.h
+++ b/tools/objtool/include/objtool/klp.h
@@ -43,9 +43,14 @@ struct klp_symid {
 };
 
 struct objtool_file;
+struct elf;
+struct symbol;
 
 int klp_create_symid_sections(struct objtool_file *file);
 
+int klp_sympos_init(struct elf *orig);
+unsigned long klp_find_sympos(struct elf *elf, struct symbol *sym);
+
 int cmd_klp_checksum(int argc, const char **argv);
 int cmd_klp_diff(int argc, const char **argv);
 int cmd_klp_post_link(int argc, const char **argv);
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 75ba0e060a34d..c5284d2752072 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -898,65 +898,6 @@ static int correlate_symbols(struct elfs *e)
 	return 0;
 }
 
-/* "sympos" is used by livepatch to disambiguate duplicate symbol names */
-static unsigned long find_sympos(struct elf *elf, struct symbol *sym)
-{
-	bool vmlinux = str_ends_with(objname, "vmlinux.o");
-	unsigned long sympos = 0, nr_matches = 0;
-	bool has_dup = false;
-	struct symbol *s;
-
-	if (sym->bind != STB_LOCAL)
-		return 0;
-
-	if (vmlinux && is_func_sym(sym)) {
-		/*
-		 * HACK: Unfortunately, symbol ordering can differ between
-		 * vmlinux.o and vmlinux due to the linker script emitting
-		 * .text.unlikely* before .text*.  Count .text.unlikely* first.
-		 *
-		 * TODO: Disambiguate symbols more reliably (checksums?)
-		 */
-		for_each_sym(elf, s) {
-			if (strstarts(s->sec->name, ".text.unlikely") &&
-			    !strcmp(s->name, sym->name)) {
-				nr_matches++;
-				if (s == sym)
-					sympos = nr_matches;
-				else
-					has_dup = true;
-			}
-		}
-		for_each_sym(elf, s) {
-			if (!strstarts(s->sec->name, ".text.unlikely") &&
-			    !strcmp(s->name, sym->name)) {
-				nr_matches++;
-				if (s == sym)
-					sympos = nr_matches;
-				else
-					has_dup = true;
-			}
-		}
-	} else {
-		for_each_sym(elf, s) {
-			if (!strcmp(s->name, sym->name)) {
-				nr_matches++;
-				if (s == sym)
-					sympos = nr_matches;
-				else
-					has_dup = true;
-			}
-		}
-	}
-
-	if (!sympos) {
-		ERROR("can't find sympos for %s", sym->name);
-		return ULONG_MAX;
-	}
-
-	return has_dup ? sympos : 0;
-}
-
 static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym);
 
 static struct symbol *__clone_symbol(struct elf *elf, struct symbol *patched_sym,
@@ -1418,7 +1359,7 @@ static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc,
 			return -1;
 
 		sym_orig_name = patched_sym->twin->name;
-		sympos = find_sympos(e->orig, patched_sym->twin);
+		sympos = klp_find_sympos(e->orig, patched_sym->twin);
 		if (sympos == ULONG_MAX)
 			return -1;
 	}
@@ -2036,7 +1977,7 @@ static int create_klp_sections(struct elfs *e)
 
 		/* klp_func_ext.sympos */
 		BUILD_BUG_ON(sizeof(sympos) != sizeof_field(struct klp_func_ext, sympos));
-		sympos = find_sympos(e->orig, sym->clone->twin);
+		sympos = klp_find_sympos(e->orig, sym->clone->twin);
 		if (sympos == ULONG_MAX)
 			return -1;
 		memcpy(func_data + offsetof(struct klp_func_ext, sympos), &sympos,
@@ -2190,6 +2131,9 @@ int cmd_klp_diff(int argc, const char **argv)
 	if (!e.orig || !e.patched)
 		return -1;
 
+	if (klp_sympos_init(e.orig))
+		return -1;
+
 	if (read_exports())
 		return -1;
 
diff --git a/tools/objtool/klp-sympos.c b/tools/objtool/klp-sympos.c
new file mode 100644
index 0000000000000..bbfae516d3395
--- /dev/null
+++ b/tools/objtool/klp-sympos.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Compute "sympos", the position used by livepatch to disambiguate
+ * duplicate symbol names in the patched object.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include <objtool/objtool.h>
+#include <objtool/warn.h>
+#include <objtool/endianness.h>
+#include <objtool/klp.h>
+
+#include <linux/string.h>
+
+struct vmlinux_sym {
+	struct hlist_node hash;
+	const char *name;
+	u64 addr;
+};
+
+struct vmlinux_symid {
+	struct hlist_node hash;
+	u64 id;
+	u64 addr;
+};
+
+struct vmlinux_o_symid {
+	struct hlist_node hash;
+	u64 id;
+	unsigned int sym_idx;
+};
+
+static DEFINE_HASHTABLE(vmlinux_o_symids, 16);
+
+/*
+ * The original linked kernel, found next to the orig vmlinux.o.  Read with raw
+ * libelf rather than elf_open_read(): only the symbol table and the resolved
+ * .klp.symid table are needed, not the (huge) instruction/reloc machinery.
+ *
+ * Both tables are built once by read_orig_vmlinux().  The Elf handle stays
+ * open because the hashed names point into its mmapped string table.
+ */
+static struct {
+	Elf *elf;
+	DECLARE_HASHTABLE(syms, 16);	/* name -> address */
+	DECLARE_HASHTABLE(symids, 16);	/* .klp.symid id -> address */
+} vmlinux;
+
+/*
+ * Would the symbol be visible to the runtime's kallsyms-based symbol lookup?
+ */
+static bool vmlinux_sym_in_kallsyms(Elf *elf, GElf_Sym *sym)
+{
+	unsigned int type = GELF_ST_TYPE(sym->st_info);
+	GElf_Shdr shdr;
+	Elf_Scn *scn;
+
+	if (sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE)
+		return false;
+
+	if (type == STT_SECTION || type == STT_FILE)
+		return false;
+
+	scn = elf_getscn(elf, sym->st_shndx);
+	if (!scn || !gelf_getshdr(scn, &shdr))
+		return false;
+
+	return shdr.sh_flags & SHF_ALLOC;
+}
+
+static int read_orig_vmlinux(const char *filename)
+{
+	size_t shstrndx, nr_syms = 0, nr_symids = 0, strtab_idx = 0;
+	Elf_Data *symtab_data = NULL, *symid_data = NULL;
+	struct klp_symid *symids;
+	Elf_Scn *scn = NULL;
+	GElf_Ehdr ehdr;
+	int fd;
+
+	fd = open(filename, O_RDONLY);
+	if (fd == -1) {
+		ERROR_GLIBC("can't open '%s'", filename);
+		return -1;
+	}
+
+	if (elf_version(EV_CURRENT) == EV_NONE) {
+		ERROR_ELF("elf_version");
+		return -1;
+	}
+
+	vmlinux.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+	if (!vmlinux.elf) {
+		ERROR_ELF("elf_begin");
+		return -1;
+	}
+
+	if (!gelf_getehdr(vmlinux.elf, &ehdr)) {
+		ERROR_ELF("gelf_getehdr");
+		return -1;
+	}
+
+	if (elf_getshdrstrndx(vmlinux.elf, &shstrndx)) {
+		ERROR_ELF("elf_getshdrstrndx");
+		return -1;
+	}
+
+	while ((scn = elf_nextscn(vmlinux.elf, scn))) {
+		const char *name;
+		GElf_Shdr shdr;
+
+		if (!gelf_getshdr(scn, &shdr)) {
+			ERROR_ELF("gelf_getshdr");
+			return -1;
+		}
+
+		if (shdr.sh_type == SHT_SYMTAB) {
+			symtab_data = elf_getdata(scn, NULL);
+			if (!symtab_data) {
+				ERROR_ELF("elf_getdata");
+				return -1;
+			}
+			nr_syms = shdr.sh_size / shdr.sh_entsize;
+			strtab_idx = shdr.sh_link;
+			continue;
+		}
+
+		name = elf_strptr(vmlinux.elf, shstrndx, shdr.sh_name);
+		if (name && !strcmp(name, KLP_SYMID_SEC)) {
+			if (shdr.sh_size % sizeof(struct klp_symid)) {
+				ERROR("%s: %s: struct klp_symid size mismatch",
+				      filename, KLP_SYMID_SEC);
+				return -1;
+			}
+			symid_data = elf_getdata(scn, NULL);
+			if (!symid_data) {
+				ERROR_ELF("elf_getdata");
+				return -1;
+			}
+			nr_symids = shdr.sh_size / sizeof(struct klp_symid);
+		}
+	}
+
+	if (!symtab_data) {
+		ERROR("%s: missing symbol table", filename);
+		return -1;
+	}
+
+	if (!symid_data) {
+		ERROR("%s: missing %s section, kernel not built with CONFIG_KLP_BUILD?",
+		      filename, KLP_SYMID_SEC);
+		return -1;
+	}
+
+	for (size_t i = 0; i < nr_syms; i++) {
+		struct vmlinux_sym *vsym;
+		const char *name;
+		GElf_Sym s;
+
+		if (!gelf_getsym(symtab_data, i, &s)) {
+			ERROR_ELF("gelf_getsym");
+			return -1;
+		}
+
+		if (!vmlinux_sym_in_kallsyms(vmlinux.elf, &s))
+			continue;
+
+		name = elf_strptr(vmlinux.elf, strtab_idx, s.st_name);
+		if (!name)
+			continue;
+
+		vsym = calloc(1, sizeof(*vsym));
+		if (!vsym) {
+			ERROR_GLIBC("calloc");
+			return -1;
+		}
+
+		vsym->name = name;
+		vsym->addr = s.st_value;
+		hash_add(vmlinux.syms, &vsym->hash, str_hash(name));
+	}
+
+	symids = symid_data->d_buf;
+
+	for (size_t i = 0; i < nr_symids; i++) {
+		struct vmlinux_symid *vsymid;
+
+		vsymid = calloc(1, sizeof(*vsymid));
+		if (!vsymid) {
+			ERROR_GLIBC("calloc");
+			return -1;
+		}
+
+		vsymid->id = __bswap_if_needed(&ehdr, symids[i].id);
+		vsymid->addr = __bswap_if_needed(&ehdr, symids[i].addr);
+		hash_add(vmlinux.symids, &vsymid->hash, vsymid->id);
+	}
+
+	/* the fd and Elf handle stay open, the hashed names live in the mmap */
+	return 0;
+}
+
+/*
+ * Read the orig vmlinux.o's .klp.symid table, an array of entries whose 'addr'
+ * fields have relocs to the symbols they describe.
+ */
+static int read_vmlinux_o_symids(struct elf *vmlinux_o)
+{
+	struct section *sec;
+
+	for_each_sec(vmlinux_o, sec) {
+		unsigned long nr;
+
+		if (strcmp(sec->name, KLP_SYMID_SEC))
+			continue;
+
+		if (sec_size(sec) % sizeof(struct klp_symid)) {
+			ERROR("%s: %s: struct klp_symid size mismatch",
+			      vmlinux_o->name, KLP_SYMID_SEC);
+			return -1;
+		}
+
+		nr = sec_size(sec) / sizeof(struct klp_symid);
+
+		for (unsigned long i = 0; i < nr; i++) {
+			unsigned long offset = i * sizeof(struct klp_symid);
+			struct vmlinux_o_symid *entry;
+			struct klp_symid *symid;
+			struct reloc *reloc;
+
+			entry = calloc(1, sizeof(*entry));
+			if (!entry) {
+				ERROR_GLIBC("calloc");
+				return -1;
+			}
+
+			symid = sec->data->d_buf + offset;
+			entry->id = bswap_if_needed(vmlinux_o, symid->id);
+
+			reloc = find_reloc_by_dest(vmlinux_o, sec,
+						   offset + offsetof(struct klp_symid, addr));
+			if (!reloc) {
+				ERROR("%s: missing reloc for %s entry",
+				      vmlinux_o->name, KLP_SYMID_SEC);
+				return -1;
+			}
+			entry->sym_idx = reloc->sym->idx;
+
+			hash_add(vmlinux_o_symids, &entry->hash, entry->sym_idx);
+		}
+	}
+
+	return 0;
+}
+
+int klp_sympos_init(struct elf *orig)
+{
+	char *filename;
+	int ret;
+
+	if (!str_ends_with(objname, "vmlinux.o"))
+		return 0;
+
+	if (read_vmlinux_o_symids(orig))
+		return -1;
+
+	filename = strndup(objname, strlen(objname) - 2);
+	if (!filename) {
+		ERROR_GLIBC("strndup");
+		return -1;
+	}
+
+	ret = read_orig_vmlinux(filename);
+	free(filename);
+
+	return ret;
+}
+
+/* Find the symbol's id in the orig vmlinux.o's .klp.symid table */
+static int find_vmlinux_o_symid(struct symbol *sym, u64 *id)
+{
+	struct vmlinux_o_symid *entry;
+
+	hash_for_each_possible(vmlinux_o_symids, entry, hash, sym->idx) {
+		if (entry->sym_idx == sym->idx) {
+			*id = entry->id;
+			return 0;
+		}
+	}
+
+	ERROR("no %s entry for symbol %s in orig vmlinux.o", KLP_SYMID_SEC,
+	      sym->name);
+	return -1;
+}
+
+/* Find the symbol's final address in the orig vmlinux's .klp.symid table */
+static int find_vmlinux_symid_addr(u64 id, u64 *addr)
+{
+	struct vmlinux_symid *symid;
+
+	hash_for_each_possible(vmlinux.symids, symid, hash, id) {
+		if (symid->id == id) {
+			*addr = symid->addr;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+/*
+ * Find the sympos of a vmlinux-local symbol by ranking its final address
+ * among the duplicately named symbols in the linked orig vmlinux, replicating
+ * the order in which kallsyms_on_each_match_symbol() counts them.
+ */
+static unsigned long find_vmlinux_sympos(struct symbol *sym)
+{
+	unsigned long nr_matches = 0, sympos = 1;
+	u32 key = str_hash(sym->name);
+	struct vmlinux_sym *vsym;
+	bool found = false;
+	u64 id, addr;
+
+	hash_for_each_possible(vmlinux.syms, vsym, hash, key)
+		if (!strcmp(vsym->name, sym->name))
+			nr_matches++;
+
+	if (!nr_matches) {
+		ERROR("can't find symbol %s in orig vmlinux", sym->name);
+		return ULONG_MAX;
+	}
+
+	/*
+	 * Unique symbols don't need disambiguating.  They also have no
+	 * .klp.symid entry, which is only emitted for names duplicated in
+	 * vmlinux.o, so the lookups below would fail.
+	 */
+	if (nr_matches == 1)
+		return 0;
+
+	if (find_vmlinux_o_symid(sym, &id))
+		return ULONG_MAX;
+
+	if (find_vmlinux_symid_addr(id, &addr)) {
+		ERROR("no %s entry for symbol %s in orig vmlinux", KLP_SYMID_SEC,
+		      sym->name);
+		return ULONG_MAX;
+	}
+
+	hash_for_each_possible(vmlinux.syms, vsym, hash, key) {
+		if (strcmp(vsym->name, sym->name))
+			continue;
+
+		if (vsym->addr < addr)
+			sympos++;
+		else if (vsym->addr == addr)
+			found = true;
+	}
+
+	if (!found) {
+		ERROR("%s address mismatch for symbol %s, stale orig vmlinux?",
+		      KLP_SYMID_SEC, sym->name);
+		return ULONG_MAX;
+	}
+
+	return sympos;
+}
+
+/*
+ * "sympos" is used by livepatch to disambiguate duplicate symbol names.
+ */
+unsigned long klp_find_sympos(struct elf *elf, struct symbol *sym)
+{
+	unsigned long sympos = 0, nr_matches = 0;
+	bool has_dup = false;
+	struct symbol *s;
+
+	if (sym->bind != STB_LOCAL)
+		return 0;
+
+	/*
+	 * vmlinux: the final link reorders symbols relative to vmlinux.o,
+	 * so the position needs to be derived from the linked orig vmlinux via
+	 * the .klp.symid table.
+	 */
+	if (vmlinux.elf)
+		return find_vmlinux_sympos(sym);
+
+	/*
+	 * modules: the final .ko preserves symbol table order, so a
+	 * symtab-order count here matches the runtime count done by
+	 * module_kallsyms_on_each_symbol().
+	 */
+	for_each_sym(elf, s) {
+		if (!strcmp(s->name, sym->name)) {
+			nr_matches++;
+			if (s == sym)
+				sympos = nr_matches;
+			else
+				has_dup = true;
+		}
+	}
+
+	if (!sympos) {
+		ERROR("can't find sympos for %s", sym->name);
+		return ULONG_MAX;
+	}
+
+	return has_dup ? sympos : 0;
+}
-- 
2.53.0




^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-12  8:22 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260912065648.999753832@linuxfoundation.org>
2026-09-12  6:45 ` [PATCH 7.2 0974/1815] objtool/klp: Fix module name normalization for paths with dots Greg Kroah-Hartman
2026-09-12  6:45 ` [PATCH 7.2 0975/1815] objtool/klp: Normalize Module.symvers paths to module names Greg Kroah-Hartman
2026-09-12  6:45 ` [PATCH 7.2 0976/1815] objtool/klp: Fix false module dependencies caused by dead relocs Greg Kroah-Hartman
2026-09-12  6:45 ` [PATCH 7.2 0977/1815] objtool/klp: Add .klp.symid for sympos disambiguation Greg Kroah-Hartman
2026-09-12  6:45 ` [PATCH 7.2 0978/1815] objtool/klp: Fix symbol resolution for duplicate data symbols Greg Kroah-Hartman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox