Live Patching
 help / color / mirror / Atom feed
From: Song Liu <song@kernel.org>
To: live-patching@vger.kernel.org
Cc: jpoimboe@kernel.org, peterz@infradead.org, jikos@kernel.org,
	mbenes@suse.cz, pmladek@suse.com, joe.lawrence@redhat.com,
	puranjay@kernel.org, kernel-team@meta.com,
	Song Liu <song@kernel.org>
Subject: [PATCH 58/58] objtool/klp: Add test for ThinLTO symbols sharing a demangled name
Date: Fri, 11 Sep 2026 11:50:31 -0700	[thread overview]
Message-ID: <20260911185031.1534046-33-song@kernel.org> (raw)
In-Reply-To: <20260911185031.1534046-1-song@kernel.org>

A file-local symbol which ThinLTO has to make visible is renamed
helper.llvm.<hash>.  With two such helpers in one link, the original and
the patched object hold two each, all four spelled differently, and
demangling gives "helper" for every one of them -- so the name alone cannot
say which corresponds to which.

Three translation units, two with a static helper of the same name and a
third calling into both, which is what forces the promotion.  Only one
helper changes: paired correctly that means exactly one is cloned, and
paired the wrong way round the other is, or both are.  Had both bodies
changed, both would be cloned either way and the test would prove nothing
-- which is how the first version of this was written.

The outcome is asserted, not the machinery.  With the clang tested here the
pairing survives disabling the .llvm.<hash> suffix map and stubbing out
llvm_suffix() entirely, so no single-line sabotage distinguishes it; the
tiered matcher this case was written for is not needed for this shape.  The
test says so rather than implying otherwise.

Assisted-by: Claude:claude-opus-4
Based-on-test-by: Joe Lawrence <joe.lawrence@redhat.com>
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
 .../generic/fixtures/thinlto_ambiguity.c      | 55 +++++++++++++
 .../tests/generic/test-thinlto-ambiguity.sh   | 77 +++++++++++++++++++
 2 files changed, 132 insertions(+)
 create mode 100644 tools/objtool/tests/generic/fixtures/thinlto_ambiguity.c
 create mode 100755 tools/objtool/tests/generic/test-thinlto-ambiguity.sh

diff --git a/tools/objtool/tests/generic/fixtures/thinlto_ambiguity.c b/tools/objtool/tests/generic/fixtures/thinlto_ambiguity.c
new file mode 100644
index 000000000000..4a87bc92ea2e
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/thinlto_ambiguity.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Three translation units linked with ThinLTO, two of which have a file-local
+ * helper of the same name.
+ *
+ * TU_C calls into both of the others, so ThinLTO imports entry_a and entry_b
+ * and with them the static helper each one calls.  A file-local symbol which
+ * has to become visible is renamed helper.llvm.<hash>, and the hash is content
+ * derived -- so the two helpers get different hashes from each other, and
+ * different ones again after the patch changes them both.
+ *
+ * That leaves klp diff with two symbols in the original and two in the patched
+ * object, all four named differently, which have to be paired up correctly.
+ * Demangling alone gives "helper" for all of them; something else has to
+ * decide which is which.
+ *
+ * Only TU_A's helper changes.  That is what makes a wrong pairing observable:
+ * paired correctly, one helper is changed and the other is not, so exactly one
+ * is cloned.  Paired the wrong way round, both look changed -- or the wrong
+ * one does.  If both bodies changed the outcome would be the same either way
+ * and the test would prove nothing.
+ *
+ * BASE differs between the two so their bodies are not identical to begin
+ * with.
+ */
+
+#if defined(TU_C)
+extern int entry_a(int x);
+extern int entry_b(int x);
+int glue(int x) { return entry_a(x) + entry_b(x + 1); }
+#else
+#ifdef TU_B
+#define ENTRY entry_b
+#define BASE 5
+#else
+#define ENTRY entry_a
+#define BASE 10
+static const char __modinfo[]
+	__attribute__((section(".modinfo"), used, aligned(1))) = "\0name=vmlinux";
+#endif
+static __attribute__((noinline)) int helper(int x, int len)
+{
+	int sum = 0, i;
+
+	for (i = 0; i < len; i++)
+#if defined(PATCHED) && !defined(TU_B)
+		sum += i * 2 + BASE;	/* only TU_A's helper changes */
+#else
+		sum += i + BASE;
+#endif
+	return sum + x;
+}
+
+int ENTRY(int x) { return helper(x, 4); }
+#endif
diff --git a/tools/objtool/tests/generic/test-thinlto-ambiguity.sh b/tools/objtool/tests/generic/test-thinlto-ambiguity.sh
new file mode 100755
index 000000000000..34f4f3a58e20
--- /dev/null
+++ b/tools/objtool/tests/generic/test-thinlto-ambiguity.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Two ThinLTO-promoted symbols sharing a demangled name must be paired up
+# correctly.
+#
+# A file-local symbol which ThinLTO has to make visible is renamed
+# helper.llvm.<hash>.  With two such helpers in one link the original and the
+# patched object hold two each, all four spelled differently, and demangling
+# gives "helper" for all of them -- so the name is not enough to say which
+# corresponds to which.
+#
+# Getting it wrong is silent and specific: the patch is built against the wrong
+# body, so one call site gets the other helper's arithmetic.  Nothing fails to
+# build and nothing fails to load.
+#
+# test-thinlto-local covers the unambiguous case, one promoted symbol whose
+# hash moved.  This is the case where demangling alone is not an answer.
+#
+# The outcome is asserted, not the machinery: with the clang tested here the
+# pairing succeeds even with the .llvm.<hash> suffix map disabled and with
+# llvm_suffix() stubbed out, so no single-line sabotage distinguishes it.  The
+# tiered matcher this case was written for is not needed for this shape.
+#
+# Covers the same ground as corpus/x86_64-llvm-thinlto/
+# thin-lto-demangled-ambiguity and thin-lto-demangled-global-match in Joe
+# Lawrence's klp-build unit test corpus.
+
+. "$(dirname "$0")/../lib.sh"
+
+setup
+clang_only "ThinLTO requires clang"
+
+find_thinlto_toolchain ||
+	probe_skip "no matching clang/lld pair for a ThinLTO link; set THIN_LD to one"
+
+build_thinlto()		# $1 output object, $2 extra flags
+{
+	local t
+	for t in "" -DTU_B -DTU_C; do
+		$THIN_CC -flto=thin -O2 -ffunction-sections -fdata-sections \
+			$2 $t -c "$FIXTURES_DIR/thinlto_ambiguity.c" \
+			-o "$workdir/tu$t.o" 2>/dev/null || return 1
+	done
+	"$THIN_LD" -r "$workdir/tu.o" "$workdir/tu-DTU_B.o" "$workdir/tu-DTU_C.o" \
+		-o "$1" 2>/dev/null || return 1
+}
+
+build_thinlto "$workdir/orig.o" "" ||
+	probe_skip "ThinLTO build failed ($THIN_CC, $THIN_LD)"
+build_thinlto "$workdir/patched.o" -DPATCHED ||
+	probe_skip "ThinLTO build failed ($THIN_CC, $THIN_LD)"
+
+# The premise: two promoted helpers per object, and exactly one of them kept
+# its hash -- the one the patch did not touch.  Without that there is nothing
+# to disambiguate.
+orig_syms="$(in_symbols orig.o    | grep -oE 'helper\.llvm\.[0-9]+' | sort -u)"
+new_syms="$( in_symbols patched.o | grep -oE 'helper\.llvm\.[0-9]+' | sort -u)"
+[ "$(echo "$orig_syms" | wc -l)" = 2 ] && [ "$(echo "$new_syms" | wc -l)" = 2 ] ||
+	probe_skip "ThinLTO did not promote two distinct helpers here"
+
+kept="$(comm -12 <(echo "$orig_syms") <(echo "$new_syms"))"
+moved="$(comm -13 <(echo "$orig_syms") <(echo "$new_syms"))"
+[ "$(echo "$kept" | wc -w)" = 1 ] && [ "$(echo "$moved" | wc -w)" = 1 ] ||
+	probe_skip "expected one helper to keep its hash and one to move"
+
+run_diff
+
+# Exactly one helper is cloned, and it is the one whose body changed.  Cloning
+# the other, or both, is what a wrong pairing looks like.
+assert_not_patched "$kept"
+
+n="$(out_sections | grep -cE '[[:space:]]\.text\.helper\.llvm\.[0-9]+[[:space:]]')"
+[ "$n" = 1 ] ||
+	fail "expected 1 cloned helper, found $n"
+
+pass "ThinLTO helpers sharing a demangled name paired up correctly"
-- 
2.53.0-Meta


  parent reply	other threads:[~2026-09-11 18:53 UTC|newest]

Thread overview: 77+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-11 18:42 [PATCH 00/58] Unit test framework for klp-build toolchain Song Liu
2026-09-11 18:42 ` [PATCH 01/58] objtool: Add test harness for the klp subcommands Song Liu
2026-09-11 18:42 ` [PATCH 02/58] objtool/klp: Check the klp test environment once, before any test Song Liu
2026-09-11 19:02   ` sashiko-bot
2026-09-11 18:42 ` [PATCH 03/58] objtool/klp: Group the klp tests by architecture Song Liu
2026-09-11 18:42 ` [PATCH 04/58] objtool/klp: Classify klp test outcomes Song Liu
2026-09-11 18:42 ` [PATCH 05/58] objtool/klp: Build klp test fixtures through the harness Song Liu
2026-09-11 18:42 ` [PATCH 06/58] objtool/klp: Grow the klp test harness vocabulary Song Liu
2026-09-11 19:03   ` sashiko-bot
2026-09-11 18:42 ` [PATCH 07/58] objtool/klp: Give each run one working directory, one per test inside it Song Liu
2026-09-11 18:42 ` [PATCH 08/58] objtool/klp: Run the klp tests under set -u Song Liu
2026-09-11 18:42 ` [PATCH 09/58] objtool/klp: Document the klp test harness Song Liu
2026-09-11 19:00   ` sashiko-bot
2026-09-11 18:42 ` [PATCH 10/58] objtool: Keep failing test workdirs by default Song Liu
2026-09-11 19:07   ` sashiko-bot
2026-09-11 18:42 ` [PATCH 11/58] objtool: Forward toolchain variables to the klp test runner Song Liu
2026-09-11 18:42 ` [PATCH 12/58] objtool/klp: Add test for rejecting changed data Song Liu
2026-09-11 18:42 ` [PATCH 13/58] objtool/klp: Add test for newly introduced data Song Liu
2026-09-11 19:07   ` sashiko-bot
2026-09-11 18:42 ` [PATCH 14/58] objtool/klp: Add test for newly introduced functions Song Liu
2026-09-11 18:42 ` [PATCH 15/58] objtool/klp: Add test for static local correlation Song Liu
2026-09-11 18:42 ` [PATCH 16/58] objtool/klp: Add test for cold function halves Song Liu
2026-09-11 19:07   ` sashiko-bot
2026-09-11 18:42 ` [PATCH 17/58] objtool/klp: Add test for special section extraction Song Liu
2026-09-11 18:42 ` [PATCH 18/58] objtool/klp: Add test for selective " Song Liu
2026-09-11 18:42 ` [PATCH 19/58] objtool/klp: Add test for jump table key relocations Song Liu
2026-09-11 18:42 ` [PATCH 20/58] objtool/klp: Add test for rejecting module-owned static branch keys Song Liu
2026-09-11 18:42 ` [PATCH 21/58] objtool/klp: Add test for rejecting module-owned static call keys Song Liu
2026-09-11 18:42 ` [PATCH 22/58] objtool/klp: Add test for symids in discarded sections Song Liu
2026-09-11 18:42 ` [PATCH 23/58] objtool/klp: Add test for rejecting references to init code/data Song Liu
2026-09-11 19:18   ` sashiko-bot
2026-09-11 18:42 ` [PATCH 24/58] objtool/klp: Add test for correlation across ThinLTO name mangling Song Liu
2026-09-11 18:42 ` [PATCH 25/58] objtool/klp: Add test for objects without .modinfo Song Liu
2026-09-11 18:49 ` [PATCH 26/58] objtool/klp: Add test for unchecksummed input Song Liu
2026-09-11 18:50   ` [PATCH 27/58] objtool/klp: Add klp diff and post-link regression tests Song Liu
2026-09-11 18:50   ` [PATCH 28/58] objtool/klp: Add test for klp reloc section naming in module objects Song Liu
2026-09-11 18:50   ` [PATCH 29/58] objtool/klp: Add test for vmlinux relocs in a patched module Song Liu
2026-09-11 18:50   ` [PATCH 30/58] objtool/klp: Add test for Module.symvers path normalization Song Liu
2026-09-11 18:50   ` [PATCH 31/58] objtool/klp: Add test for the contents of the klp_funcs list Song Liu
2026-09-11 18:50   ` [PATCH 32/58] objtool/klp: Add test for EXPORT_SYMBOL_FOR_MODULES references Song Liu
2026-09-11 18:50   ` [PATCH 33/58] objtool/klp: Add test for new references to exported symbols Song Liu
2026-09-11 18:50   ` [PATCH 34/58] objtool/klp: Add test for empty x86 alternative replacements Song Liu
2026-09-11 18:50   ` [PATCH 35/58] objtool/klp: Add test for recorded checksum values Song Liu
2026-09-11 18:50   ` [PATCH 36/58] objtool/klp: Add test for position-independent checksums Song Liu
2026-09-11 19:16     ` sashiko-bot
2026-09-11 18:50   ` [PATCH 37/58] objtool/klp: Add test for sympos in module objects Song Liu
2026-09-11 19:21     ` sashiko-bot
2026-09-11 18:50   ` [PATCH 38/58] objtool/klp: Add test for sympos resolved against a linked vmlinux Song Liu
2026-09-11 18:50   ` [PATCH 39/58] objtool/klp: Add test for static locals which must not be correlated Song Liu
2026-09-11 18:50   ` [PATCH 40/58] objtool/klp: Add test for __bug_table, __ex_table and __mcount_loc extraction Song Liu
2026-09-11 19:20     ` sashiko-bot
2026-09-11 18:50   ` [PATCH 41/58] objtool/klp: Add test for kCFI prefix symbols and traps Song Liu
2026-09-11 19:21     ` sashiko-bot
2026-09-11 18:50   ` [PATCH 42/58] objtool/klp: Add test for symbols whose linkage the patch changes Song Liu
2026-09-11 18:50   ` [PATCH 43/58] objtool/klp: Test rejection of a file-local static branch key Song Liu
2026-09-11 18:50   ` [PATCH 44/58] objtool/klp: Test a hand-built livepatch module's static call keys Song Liu
2026-09-11 18:50   ` [PATCH 45/58] objtool/klp: Test text annotations on alternative replacements Song Liu
2026-09-11 18:50   ` [PATCH 46/58] objtool/klp: Add test for data object checksums Song Liu
2026-09-11 18:50   ` [PATCH 47/58] objtool/klp: Add test for symbols with no checksum entry of their own Song Liu
2026-09-11 19:23     ` sashiko-bot
2026-09-11 18:50   ` [PATCH 48/58] objtool/klp: Add test for a static branch introduced by the patch Song Liu
2026-09-11 18:50   ` [PATCH 49/58] objtool/klp: Add test for tracepoint and pr_debug static branch keys Song Liu
2026-09-11 18:50   ` [PATCH 50/58] objtool/klp: Add test for a static call introduced by the patch Song Liu
2026-09-11 19:25     ` sashiko-bot
2026-09-11 18:50   ` [PATCH 51/58] objtool/klp: Add test for instruction operand checksums Song Liu
2026-09-11 18:50   ` [PATCH 52/58] objtool/klp: Add test for alternative replacement code in checksums Song Liu
2026-09-11 19:30     ` sashiko-bot
2026-09-11 18:50   ` [PATCH 53/58] objtool/klp: Add test for the alignment of cloned data sections Song Liu
2026-09-11 18:50   ` [PATCH 54/58] objtool/klp: Add test for a patch which strips a data annotation Song Liu
2026-09-11 19:24     ` sashiko-bot
2026-09-11 18:50   ` [PATCH 55/58] objtool/klp: Add test for absolute and __ADDRESSABLE symbols Song Liu
2026-09-11 18:50   ` [PATCH 56/58] objtool/klp: Add test for UBSAN metadata in an unchanged function Song Liu
2026-09-11 18:50   ` [PATCH 57/58] objtool/klp: Add test for Clang switch jump tables Song Liu
2026-09-11 19:27     ` sashiko-bot
2026-09-11 18:50   ` Song Liu [this message]
2026-09-11 19:28     ` [PATCH 58/58] objtool/klp: Add test for ThinLTO symbols sharing a demangled name sashiko-bot
2026-09-13  1:53 ` [PATCH 00/58] Unit test framework for klp-build toolchain Josh Poimboeuf

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=20260911185031.1534046-33-song@kernel.org \
    --to=song@kernel.org \
    --cc=jikos@kernel.org \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@kernel.org \
    --cc=kernel-team@meta.com \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=peterz@infradead.org \
    --cc=pmladek@suse.com \
    --cc=puranjay@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