mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Longjun Luo <luolongjuna@gmail.com>
To: jpoimboe@kernel.org, peterz@infradead.org
Cc: mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com,
	song@kernel.org, puranjay@kernel.org, nathan@kernel.org,
	ndesaulniers@google.com, morbo@google.com,
	justinstitt@google.com, live-patching@vger.kernel.org,
	llvm@lists.linux.dev, linux-kernel@vger.kernel.org,
	Longjun Luo <luolongjuna@gmail.com>
Subject: [RFC PATCH v2 09/11] objtool/klp: Extend twins through unchanged function references
Date: Wed,  4 Nov 2026 21:31:55 +0800	[thread overview]
Message-ID: <20261104133158.3187525-10-luolongjuna@gmail.com> (raw)
In-Reply-To: <20261104133158.3187525-1-luolongjuna@gmail.com>

Repeated statics and optimized variable pieces can share an identity
and checksum, leaving them uncorrelated after identity matching.

For paired functions with equal checksums, compare their relocation
entries and extend twins to the referenced symbols. Require matching
reference layouts and compatible targets, and reject twin conflicts.

This also checks the correlation against the function checksum: equal
checksums can hide a switch between instances sharing one identity, and
a conflicting reference must stop patch generation.

Validated the available objtool tests with GCC 16.2.1 and Clang
22.1.8. Only local-to-global-flip, sympos and sympos-vmlinux fail,
reflecting the linkage and duplicate-identity policies.

Assisted-by: LLM
Signed-off-by: Longjun Luo <luolongjuna@gmail.com>
---
 tools/objtool/klp-diff.c | 132 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 132 insertions(+)

diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 468b8654b687..e8252e586260 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -826,6 +826,8 @@ static int validate_symbol_twins(struct elfs *e)
 	return ret;
 }
 
+static int extend_twins_by_relocations(struct elfs *e);
+
 /*
  * Correlate symbols between the orig and patched objects.  This is a
  * prerequisite for detecting changed functions, as well as for properly
@@ -845,6 +847,7 @@ static int correlate_symbols(struct elfs *e)
 
 	if (correlate_by_identity(e) ||
 	    extend_same_twins_by_checksum(e) ||
+	    extend_twins_by_relocations(e) ||
 	    validate_symbol_twins(e))
 		return -1;
 
@@ -1294,6 +1297,135 @@ static int convert_reloc_sym(struct elf *elf, struct reloc *reloc)
 	return convert_reloc_secsym_to_sym(elf, reloc);
 }
 
+static int correlate_reloc(struct elfs *e, struct symbol *func,
+			   struct reloc *orig_reloc, struct reloc *patched_reloc)
+{
+	struct symbol *orig_sym, *patched_sym;
+	unsigned long offset = reloc_offset(orig_reloc) - func->offset;
+
+	if (offset != reloc_offset(patched_reloc) - func->twin->offset ||
+	    reloc_type(orig_reloc) != reloc_type(patched_reloc)) {
+		ERROR("%s+%#lx: relocation mismatch in unchanged function", func->name, offset);
+		return -1;
+	}
+
+	if (reloc_type(orig_reloc) == R_NONE ||
+	    is_uncorrelated_section(orig_reloc->sym->sec) ||
+	    is_uncorrelated_section(patched_reloc->sym->sec))
+		return 0;
+
+	/* Section symbols have no twins; resolve the target symbol first. */
+	if (convert_reloc_secsym_to_sym(e->orig, orig_reloc) ||
+	    convert_reloc_secsym_to_sym(e->patched, patched_reloc))
+		return 0;
+
+	orig_sym = orig_reloc->sym;
+	patched_sym = patched_reloc->sym;
+	if (orig_sym->dont_correlate || patched_sym->dont_correlate)
+		return 0;
+
+	if (reloc_addend(orig_reloc) != reloc_addend(patched_reloc)) {
+		ERROR("%s+%#lx: reference offset mismatch: %s[%u]%+lld and %s[%u]%+lld",
+		      func->name, offset,
+		      orig_sym->name, orig_sym->idx, (long long)reloc_addend(orig_reloc),
+		      patched_sym->name, patched_sym->idx, (long long)reloc_addend(patched_reloc));
+		return -1;
+	}
+
+	if (orig_sym->twin == patched_sym && patched_sym->twin == orig_sym)
+		return 0;
+
+	if (orig_sym->twin || patched_sym->twin) {
+		ERROR("%s+%#lx: twin conflict: orig %s[%u]->%s[%u], patched %s[%u]->%s[%u]",
+		      func->name, offset,
+		      orig_sym->name, orig_sym->idx,
+		      orig_sym->twin ? orig_sym->twin->name : "<none>",
+		      orig_sym->twin ? orig_sym->twin->idx : 0,
+		      patched_sym->name, patched_sym->idx,
+		      patched_sym->twin ? patched_sym->twin->name : "<none>",
+		      patched_sym->twin ? patched_sym->twin->idx : 0);
+		return -1;
+	}
+
+	/* Compound target checksums are independent of their defining TU. */
+	if (is_compound_constant(orig_sym) && is_compound_constant(patched_sym)) {
+		if (!orig_sym->csum.checksum || orig_sym->len != patched_sym->len ||
+		    orig_sym->csum.checksum != patched_sym->csum.checksum) {
+			ERROR("%s+%#lx: compound content mismatch in unchanged function",
+			      func->name, offset);
+			return -1;
+		}
+	} else if (!is_twin_candidate(orig_sym, patched_sym)) {
+		ERROR("%s+%#lx: incompatible reference targets: %s[%u] and %s[%u]",
+		      func->name, offset, orig_sym->name, orig_sym->idx,
+		      patched_sym->name, patched_sym->idx);
+		return -1;
+	}
+
+	orig_sym->twin = patched_sym;
+	patched_sym->twin = orig_sym;
+	dbg_correlate("%s(): %s+%#lx: %s[%u] -> %s[%u]",
+		      __func__, func->name, offset,
+		      orig_sym->name, orig_sym->idx, patched_sym->name, patched_sym->idx);
+	return 0;
+}
+
+static struct reloc *get_func_relocs(struct elf *elf, struct symbol *func, unsigned int *nr)
+{
+	struct reloc *first, *reloc;
+
+	first = find_reloc_by_dest_range(elf, func->sec, func->offset, func->len);
+	*nr = 0;
+	for (reloc = first; reloc && reloc_offset(reloc) < func->offset + func->len;
+	     reloc = rsec_next_reloc(func->sec->rsec, reloc))
+		(*nr)++;
+	return first;
+}
+
+/* Use unchanged functions' references to resolve ambiguous symbol identities. */
+static int extend_twin_by_relocations(struct elfs *e, struct symbol *func)
+{
+	struct reloc *orig_relocs, *patched_relocs;
+	unsigned int nr_orig, nr_patched, i;
+
+	orig_relocs = get_func_relocs(e->orig, func, &nr_orig);
+	patched_relocs = get_func_relocs(e->patched, func->twin, &nr_patched);
+	if (nr_orig != nr_patched) {
+		ERROR("%s: relocation count mismatch in unchanged function: %u != %u",
+		      func->name, nr_orig, nr_patched);
+		return -1;
+	}
+
+	for (i = 0; i < nr_orig; i++)
+		if (correlate_reloc(e, func, &orig_relocs[i], &patched_relocs[i]))
+			return -1;
+	return 0;
+}
+
+static bool same_function_checksum(struct symbol *func)
+{
+	/* Cold subfunctions have no separate checksum; use their parent's. */
+	struct symbol *parent = func->pfunc;
+	struct symbol *parent_twin = func->twin->pfunc;
+
+	return parent->twin == parent_twin && parent->csum.checksum &&
+	       parent->csum.checksum == parent_twin->csum.checksum;
+}
+
+static int extend_twins_by_relocations(struct elfs *e)
+{
+	struct symbol *sym;
+
+	for_each_sym(e->orig, sym) {
+		if (!is_func_sym(sym) || sym->dont_correlate || !sym->twin ||
+		    !same_function_checksum(sym))
+			continue;
+		if (extend_twin_by_relocations(e, sym))
+			return -1;
+	}
+	return 0;
+}
+
 /*
  * Check if the original module already has a dependency on dep_mod, i.e. it
  * already references at least one export from that module.
-- 
2.55.0


  parent reply	other threads:[~2026-11-04 13:33 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-04 13:31 [RFC PATCH v2 00/11] objtool/klp: DWARF-based cross-build symbol correlation Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 01/11] objtool/klp: Fix checksums and copying of wide strings Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 02/11] objtool/klp: Hash compound literals by content Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 03/11] objtool/klp: Preserve identifier digits when stripping suffixes Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 04/11] objtool/klp: Read symbol identities from DWARF Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 05/11] objtool/klp: Support reduced and split DWARF Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 06/11] klp-build: Require CONFIG_DEBUG_INFO Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 07/11] objtool/klp: Correlate symbols using DWARF identities Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 08/11] objtool/klp: Include symbol identities in reference checksums Longjun Luo
2026-11-04 13:31 ` Longjun Luo [this message]
2026-11-04 13:31 ` [RFC PATCH v2 10/11] objtool/klp: Check unmatched data references Longjun Luo
2026-11-04 13:31 ` [RFC PATCH v2 11/11] objtool/klp: Test symbol identity and state preservation Longjun Luo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261104133158.3187525-10-luolongjuna@gmail.com \
    --to=luolongjuna@gmail.com \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@kernel.org \
    --cc=justinstitt@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=llvm@lists.linux.dev \
    --cc=mbenes@suse.cz \
    --cc=morbo@google.com \
    --cc=nathan@kernel.org \
    --cc=ndesaulniers@google.com \
    --cc=peterz@infradead.org \
    --cc=pmladek@suse.com \
    --cc=puranjay@kernel.org \
    --cc=song@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®