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 3/3] objtool/klp: Test DWARF identities and reference checksums
Date: Mon, 26 Oct 2026 18:19:16 +0800	[thread overview]
Message-ID: <20261026101916.3823942-4-luolongjuna@gmail.com> (raw)
In-Reply-To: <20261026101916.3823942-1-luolongjuna@gmail.com>

Cover same-named static state in different scopes and TUs, compiler
renumbering, ThinLTO promotion and name drift, Full LTO, and newly
added source files. Check that patched functions reference their own
original state and that new functions use new state. Verify that
read-only descriptors retain their original addresses and reject
changes to their contents.

Also cover calls, data references and function-pointer tables switching
between same-named local and external targets, and an external caller
moving between TUs.

Tested on x86-64 with tools/objtool/tests/run-tests.sh:

  GCC 16.2.1:   51 passed, 3 failed, 7 skipped.
  Clang 22.1.8: 57 passed, 3 failed, 1 skipped.

All nine new tests pass with Clang; GCC passes six and skips the three
Clang LTO tests. Both DWARF 4 and 5 were exercised. Without the
reference checksum patch, only the two new checksum tests fail.

The three tests covering behavior that this series rejects by policy
still fail: local-to-global-flip (source-level linkage change), and
sympos and sympos-vmlinux (duplicate identities from compiling the same
source into multiple objects). They are left unchanged for discussion;
if the policy is accepted, they will be updated to expect the rejection.

Assisted-by: LLM
Signed-off-by: Longjun Luo <luolongjuna@gmail.com>
---
 .../tests/generic/fixtures/identity/common.h  | 15 +++++
 .../generic/fixtures/identity/consumer.c      | 30 +++++++++
 .../tests/generic/fixtures/identity/entries.c | 10 +++
 .../generic/fixtures/identity/full_left.c     | 16 +++++
 .../generic/fixtures/identity/full_right.c    |  6 ++
 .../generic/fixtures/identity/move_left.c     |  9 +++
 .../generic/fixtures/identity/move_right.c    |  8 +++
 .../generic/fixtures/identity/new/same.c      |  6 ++
 .../generic/fixtures/identity/new_driver.c    | 15 +++++
 .../generic/fixtures/identity/new_provider.c  |  4 ++
 .../generic/fixtures/identity/old/same.c      |  6 ++
 .../fixtures/identity/promotion_bounds_a.c    | 12 ++++
 .../fixtures/identity/promotion_bounds_b.c    | 12 ++++
 .../fixtures/identity/promotion_bounds_d.c    |  8 +++
 .../generic/fixtures/identity/provider.c      |  9 +++
 .../generic/fixtures/identity/readonly.c      | 30 +++++++++
 .../tests/generic/fixtures/identity/record.h  |  8 +++
 .../tests/generic/fixtures/identity/rx.c      |  4 ++
 .../generic/fixtures/identity/source_driver.c | 11 ++++
 .../generic/fixtures/identity/static_scope.c  | 31 +++++++++
 .../fixtures/identity/static_scope_reorder.c  | 31 +++++++++
 .../tests/generic/fixtures/identity/tx.c      |  4 ++
 .../generic/test-checksum-identity-source.sh  | 24 +++++++
 .../tests/generic/test-checksum-identity.sh   | 31 +++++++++
 .../tests/generic/test-identity-full-lto.sh   | 28 ++++++++
 .../tests/generic/test-identity-new-tu.sh     | 22 +++++++
 .../generic/test-identity-readonly-address.sh | 23 +++++++
 .../tests/generic/test-identity-source.sh     | 26 ++++++++
 .../generic/test-identity-static-scope.sh     | 35 ++++++++++
 .../test-identity-thinlto-name-drift.sh       | 65 +++++++++++++++++++
 .../test-identity-thinlto-promotion.sh        | 25 +++++++
 tools/objtool/tests/identity-lib.sh           | 65 +++++++++++++++++++
 32 files changed, 629 insertions(+)
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/common.h
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/consumer.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/entries.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/full_left.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/full_right.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/move_left.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/move_right.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/new/same.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/new_driver.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/new_provider.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/old/same.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/promotion_bounds_a.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/promotion_bounds_b.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/promotion_bounds_d.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/provider.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/readonly.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/record.h
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/rx.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/source_driver.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/static_scope.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/static_scope_reorder.c
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/tx.c
 create mode 100755 tools/objtool/tests/generic/test-checksum-identity-source.sh
 create mode 100755 tools/objtool/tests/generic/test-checksum-identity.sh
 create mode 100755 tools/objtool/tests/generic/test-identity-full-lto.sh
 create mode 100755 tools/objtool/tests/generic/test-identity-new-tu.sh
 create mode 100755 tools/objtool/tests/generic/test-identity-readonly-address.sh
 create mode 100755 tools/objtool/tests/generic/test-identity-source.sh
 create mode 100755 tools/objtool/tests/generic/test-identity-static-scope.sh
 create mode 100755 tools/objtool/tests/generic/test-identity-thinlto-name-drift.sh
 create mode 100755 tools/objtool/tests/generic/test-identity-thinlto-promotion.sh
 create mode 100644 tools/objtool/tests/identity-lib.sh

diff --git a/tools/objtool/tests/generic/fixtures/identity/common.h b/tools/objtool/tests/generic/fixtures/identity/common.h
new file mode 100644
index 000000000000..d0f56751fcd9
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/common.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#ifndef KLP_TEST_IDENTITY_H
+#define KLP_TEST_IDENTITY_H
+
+#ifdef __clang__
+/* Use noinline on Clang to keep calls visible in these tests. */
+#define NOIPA __attribute__((noinline))
+#else
+#define NOIPA __attribute__((noipa))
+#endif
+#define MODINFO static const char modinfo[] \
+	__attribute__((used, section(".modinfo"))) = "name=vmlinux"
+
+#endif
diff --git a/tools/objtool/tests/generic/fixtures/identity/consumer.c b/tools/objtool/tests/generic/fixtures/identity/consumer.c
new file mode 100644
index 000000000000..4aad0e941bc3
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/consumer.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+#ifdef PATCHED
+static NOIPA int helper(int uid) { return uid == 0 ? 0 : -1; }
+#else
+int helper(int uid);
+#endif
+#ifdef DATA
+#ifdef PATCHED
+static volatile int limit = 1500;
+#else
+extern volatile int limit;
+#endif
+#endif
+#ifdef TABLE
+struct operations { int (*check)(int); };
+struct operations ops = { .check = helper };
+#endif
+NOIPA int caller(int uid)
+{
+#ifdef DATA
+	return uid <= limit ? 0 : -90;
+#elif defined(TABLE)
+	return ops.check(uid);
+#else
+	return helper(uid);
+#endif
+}
diff --git a/tools/objtool/tests/generic/fixtures/identity/entries.c b/tools/objtool/tests/generic/fixtures/identity/entries.c
new file mode 100644
index 000000000000..53e809957c1c
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/entries.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+unsigned int tx(unsigned int n);
+unsigned int rx(unsigned int n);
+unsigned int entry_tx(unsigned int n) { return tx(n); }
+#ifdef PATCHED
+unsigned int entry_rx(unsigned int n) { return rx(n); }
+#endif
diff --git a/tools/objtool/tests/generic/fixtures/identity/full_left.c b/tools/objtool/tests/generic/fixtures/identity/full_left.c
new file mode 100644
index 000000000000..1a75df588712
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/full_left.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+static volatile int state;
+#ifdef PATCHED
+static NOIPA int helper(void) { return ++state; }
+#endif
+NOIPA int left_fn(void)
+{
+#ifdef PATCHED
+	return helper();
+#else
+	return ++state;
+#endif
+}
diff --git a/tools/objtool/tests/generic/fixtures/identity/full_right.c b/tools/objtool/tests/generic/fixtures/identity/full_right.c
new file mode 100644
index 000000000000..17583384cf32
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/full_right.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+static volatile int state;
+static NOIPA int helper(void) { return ++state; }
+NOIPA int right_fn(void) { return helper(); }
diff --git a/tools/objtool/tests/generic/fixtures/identity/move_left.c b/tools/objtool/tests/generic/fixtures/identity/move_left.c
new file mode 100644
index 000000000000..7d3b03bc87e5
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/move_left.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+static NOIPA int helper(int n) { return n <= 1500 ? 0 : -90; }
+NOIPA int keep_left(int n) { return helper(n); }
+#ifndef PATCHED
+NOIPA int caller(int n) { return helper(n); }
+#endif
diff --git a/tools/objtool/tests/generic/fixtures/identity/move_right.c b/tools/objtool/tests/generic/fixtures/identity/move_right.c
new file mode 100644
index 000000000000..9a5f82a7d9ba
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/move_right.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+static NOIPA int helper(int n) { return n <= 4096 ? 0 : -90; }
+NOIPA int keep_right(int n) { return helper(n); }
+#ifdef PATCHED
+NOIPA int caller(int n) { return helper(n); }
+#endif
diff --git a/tools/objtool/tests/generic/fixtures/identity/new/same.c b/tools/objtool/tests/generic/fixtures/identity/new/same.c
new file mode 100644
index 000000000000..54550f20fe79
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/new/same.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "../common.h"
+static volatile int bytes;
+static NOIPA int helper(int n) { return bytes += n; }
+NOIPA int account_new(int n) { return helper(n); }
diff --git a/tools/objtool/tests/generic/fixtures/identity/new_driver.c b/tools/objtool/tests/generic/fixtures/identity/new_driver.c
new file mode 100644
index 000000000000..c06a1ac27609
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/new_driver.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+#ifdef PATCHED
+int clamp(int value);
+#endif
+NOIPA int target(int value)
+{
+#ifdef PATCHED
+	return clamp(value);
+#else
+	return value;
+#endif
+}
diff --git a/tools/objtool/tests/generic/fixtures/identity/new_provider.c b/tools/objtool/tests/generic/fixtures/identity/new_provider.c
new file mode 100644
index 000000000000..69c1b3d0b6a7
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/new_provider.c
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+NOIPA int clamp(int value) { return value < 0 ? 0 : value; }
diff --git a/tools/objtool/tests/generic/fixtures/identity/old/same.c b/tools/objtool/tests/generic/fixtures/identity/old/same.c
new file mode 100644
index 000000000000..e03f14f16214
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/old/same.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "../common.h"
+static volatile int bytes;
+static NOIPA int helper(int n) { return bytes += n; }
+NOIPA int account_old(int n) { return helper(n); }
diff --git a/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_a.c b/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_a.c
new file mode 100644
index 000000000000..74eb8ac0bfc3
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_a.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+static volatile int state[4];
+
+int a_get(unsigned int index)
+{
+#ifdef PATCHED
+	if (index >= 4)
+		return 0;
+#endif
+	return state[index];
+}
diff --git a/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_b.c b/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_b.c
new file mode 100644
index 000000000000..2b96569ce6cd
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_b.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+
+int a_get(unsigned int index);
+int d_get(unsigned int index);
+
+int target(unsigned int index)
+{
+	return a_get(index) + d_get(index);
+}
diff --git a/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_d.c b/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_d.c
new file mode 100644
index 000000000000..a4d6c35f80a9
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/promotion_bounds_d.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+static volatile int state[4];
+
+int d_get(unsigned int index)
+{
+	return state[index];
+}
diff --git a/tools/objtool/tests/generic/fixtures/identity/provider.c b/tools/objtool/tests/generic/fixtures/identity/provider.c
new file mode 100644
index 000000000000..412c5cb67341
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/provider.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+NOIPA int helper(int uid)
+{
+	return uid == 0 || uid == 1000 ? 0 : -1;
+}
+
+volatile int limit = 4096;
diff --git a/tools/objtool/tests/generic/fixtures/identity/readonly.c b/tools/objtool/tests/generic/fixtures/identity/readonly.c
new file mode 100644
index 000000000000..f145d3344d60
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/readonly.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+struct protocol { int kind; };
+NOIPA const struct protocol *tcp_type(int valid)
+{
+#if defined(PATCHED) && defined(CHANGE_VALUE)
+	static const struct protocol type = { 4 };
+#else
+	static const struct protocol type = { 1 };
+#endif
+#ifdef PATCHED
+	if (!valid)
+		return (void *)0;
+#endif
+	return &type;
+}
+NOIPA const struct protocol *udp_type(int valid)
+{
+	static const struct protocol type = { 1 };
+	return &type;
+}
+#ifdef PATCHED
+NOIPA const struct protocol *unix_type(int valid)
+{
+	static const struct protocol type = { 1 };
+	return &type;
+}
+#endif
diff --git a/tools/objtool/tests/generic/fixtures/identity/record.h b/tools/objtool/tests/generic/fixtures/identity/record.h
new file mode 100644
index 000000000000..5af00486c5f4
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/record.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+static inline unsigned int record(unsigned int n)
+{
+	static volatile unsigned int state;
+
+	return state += n;
+}
diff --git a/tools/objtool/tests/generic/fixtures/identity/rx.c b/tools/objtool/tests/generic/fixtures/identity/rx.c
new file mode 100644
index 000000000000..5d9871527b08
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/rx.c
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "record.h"
+unsigned int rx(unsigned int n) { return record(n); }
diff --git a/tools/objtool/tests/generic/fixtures/identity/source_driver.c b/tools/objtool/tests/generic/fixtures/identity/source_driver.c
new file mode 100644
index 000000000000..0ebcb01ced02
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/source_driver.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+#ifdef PATCHED
+int account_new(int n);
+NOIPA int account(int n) { return account_new(n); }
+#else
+int account_old(int n);
+NOIPA int account(int n) { return account_old(n); }
+#endif
diff --git a/tools/objtool/tests/generic/fixtures/identity/static_scope.c b/tools/objtool/tests/generic/fixtures/identity/static_scope.c
new file mode 100644
index 000000000000..a467f24f1a14
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/static_scope.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+
+NOIPA int tx(int n)
+{
+	static volatile int bytes;
+
+#ifdef PATCHED
+	if (n < 0)
+		return 0;
+#endif
+	return bytes += n;
+}
+
+NOIPA int rx(int n)
+{
+	static volatile int bytes;
+
+	return bytes += n;
+}
+
+#ifdef PATCHED
+NOIPA int extra(int n)
+{
+	static volatile int bytes;
+
+	return bytes += n;
+}
+#endif
diff --git a/tools/objtool/tests/generic/fixtures/identity/static_scope_reorder.c b/tools/objtool/tests/generic/fixtures/identity/static_scope_reorder.c
new file mode 100644
index 000000000000..a451e632a6cb
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/static_scope_reorder.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+
+#ifdef PATCHED
+NOIPA int rx(int n)
+{
+	static volatile int bytes;
+
+	if (n < 0)
+		return 0;
+	return bytes += n;
+}
+#endif
+
+NOIPA int tx(int n)
+{
+	static volatile int bytes;
+
+	return bytes += n;
+}
+
+#ifndef PATCHED
+NOIPA int rx(int n)
+{
+	static volatile int bytes;
+
+	return bytes += n;
+}
+#endif
diff --git a/tools/objtool/tests/generic/fixtures/identity/tx.c b/tools/objtool/tests/generic/fixtures/identity/tx.c
new file mode 100644
index 000000000000..d536c3c63549
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/tx.c
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "record.h"
+unsigned int tx(unsigned int n) { return record(n); }
diff --git a/tools/objtool/tests/generic/test-checksum-identity-source.sh b/tools/objtool/tests/generic/test-checksum-identity-source.sh
new file mode 100755
index 000000000000..b320a57c6a76
--- /dev/null
+++ b/tools/objtool/tests/generic/test-checksum-identity-source.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# Moving an external caller changes which TU-local helper it calls.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+modes=(none)
+case "$($CC --version | head -1)" in *clang*) modes+=(thin) ;; esac
+
+for mode in "${modes[@]}"; do
+	build_identity_pair "$mode" "" identity/move_left.c identity/move_right.c
+	[ "$(in_symbols orig.o | awk '$4 == "FUNC" && $8 ~ /^helper($|.llvm.)/' | wc -l)" = 2 ] ||
+		fail "fixture lacks two TU-local helpers"
+	assert_checksum_differs caller
+	assert_checksum_matches keep_left
+	assert_checksum_matches keep_right
+	run_diff
+	assert_patched caller
+	assert_not_patched keep_left
+	assert_not_patched keep_right
+done
+
+pass "moving a caller between TUs detects the changed local target"
diff --git a/tools/objtool/tests/generic/test-checksum-identity.sh b/tools/objtool/tests/generic/test-checksum-identity.sh
new file mode 100755
index 000000000000..77248e547040
--- /dev/null
+++ b/tools/objtool/tests/generic/test-checksum-identity.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# Checksums for calls, data references and function-pointer tables must
+# distinguish same-named static and external targets.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+modes=(none)
+case "$($CC --version | head -1)" in *clang*) modes+=(thin) ;; esac
+
+for mode in "${modes[@]}"; do
+	build_identity_pair "$mode" "" identity/provider.c identity/consumer.c
+	[ "$(in_symbols patched.o | awk '$4 == "FUNC" && $8 ~ /^helper($|.llvm.)/' | wc -l)" = 2 ] ||
+		fail "fixture lacks the local and external helpers"
+	assert_checksum_differs caller
+	run_diff
+	assert_patched caller
+
+	build_identity_pair "$mode" -DTABLE identity/provider.c identity/consumer.c
+	assert_checksum_differs ops
+	run_diff 255
+	assert_diff_log 'changed data: ops'
+
+	build_identity_pair "$mode" -DDATA identity/provider.c identity/consumer.c
+	assert_checksum_differs caller
+	run_diff
+	assert_patched caller
+done
+
+pass "call, data and function-pointer targets use the full identity"
diff --git a/tools/objtool/tests/generic/test-identity-full-lto.sh b/tools/objtool/tests/generic/test-identity-full-lto.sh
new file mode 100755
index 000000000000..7d68a7de7982
--- /dev/null
+++ b/tools/objtool/tests/generic/test-identity-full-lto.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# Full LTO merges FILE entries; a new helper must not replace an existing TU's helper.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+build_identity_pair full "" identity/full_left.c identity/full_right.c
+
+[ "$(in_symbols orig.o | awk '$4 == "FUNC" && $8 ~ /^helper(\.[0-9]+)?$/' | wc -l)" = 1 ] &&
+[ "$(in_symbols patched.o | awk '$4 == "FUNC" && $8 ~ /^helper(\.[0-9]+)?$/' | wc -l)" = 2 ] ||
+	probe_skip "Full LTO did not emit two same-base helper names"
+left_helper="$(identity_relocs patched.o .rela.text.left_fn | awk '{ sub(/^\.text\./, "", $5); print $5 }')"
+right_helper="$(identity_relocs patched.o .rela.text.right_fn | awk '{ sub(/^\.text\./, "", $5); print $5 }')"
+[ -n "$left_helper" ] && [ -n "$right_helper" ] || fail "missing helper references"
+left_state="$(identity_object_for orig.o left_fn)"
+[ -n "$left_state" ] || fail "missing left state reference"
+
+run_diff
+assert_patched left_fn
+assert_not_patched right_fn
+assert_patched "$left_helper"
+assert_not_patched "$right_helper"
+assert_reloc_sym .text.left_fn "$left_helper"
+run_post_link
+assert_original_reference "$left_helper" "$left_state"
+
+pass "Full LTO helpers retain their defining TUs"
diff --git a/tools/objtool/tests/generic/test-identity-new-tu.sh b/tools/objtool/tests/generic/test-identity-new-tu.sh
new file mode 100755
index 000000000000..31b2e10de83f
--- /dev/null
+++ b/tools/objtool/tests/generic/test-identity-new-tu.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# A patch may extract validation into a newly added source file.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+build_pair identity/new_driver.c
+build_one identity/new_provider.c provider.o
+mv "$workdir/patched.o" "$workdir/driver.o"
+partial_link "$workdir/patched.o" "$workdir/driver.o" "$workdir/provider.o" || fail "partial link failed"
+
+[ "$(in_symbols patched.o | awk '$4 == "FILE"' | wc -l)" -gt "$(in_symbols orig.o | awk '$4 == "FILE"' | wc -l)" ] ||
+	fail "fixture did not add a FILE entry"
+
+run_diff
+assert_patched target
+assert_patched clamp
+assert_diff_log 'changed function: target'
+assert_diff_log 'new function: clamp'
+
+pass "new translation unit does not block existing symbol correlation"
diff --git a/tools/objtool/tests/generic/test-identity-readonly-address.sh b/tools/objtool/tests/generic/test-identity-readonly-address.sh
new file mode 100755
index 000000000000..18c49856833b
--- /dev/null
+++ b/tools/objtool/tests/generic/test-identity-readonly-address.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# Read-only descriptors still need their original pointer identity.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+build_pair identity/readonly.c
+old="$(identity_object_for orig.o tcp_type)"
+[ -n "$old" ] || fail "missing TCP descriptor reference"
+
+run_diff
+assert_patched tcp_type
+assert_patched unix_type
+assert_not_patched udp_type
+run_post_link
+assert_original_reference tcp_type "$old"
+
+build_pair identity/readonly.c -DCHANGE_VALUE
+run_diff 255
+assert_diff_log 'changed data:'
+
+pass "changed function retains the original read-only descriptor address"
diff --git a/tools/objtool/tests/generic/test-identity-source.sh b/tools/objtool/tests/generic/test-identity-source.sh
new file mode 100755
index 000000000000..1c551cb91afc
--- /dev/null
+++ b/tools/objtool/tests/generic/test-identity-source.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# old/same.c and new/same.c share the FILE name "same.c"; static state
+# from the new file must still be treated as new.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+build_pair identity/source_driver.c
+build_one identity/old/same.c old.o
+build_one identity/new/same.c new.o
+mv "$workdir/orig.o" "$workdir/driver-orig.o"
+mv "$workdir/patched.o" "$workdir/driver-patched.o"
+partial_link "$workdir/orig.o" "$workdir/driver-orig.o" "$workdir/old.o" || fail "original link failed"
+partial_link "$workdir/patched.o" "$workdir/driver-patched.o" "$workdir/new.o" || fail "patched link failed"
+for obj in orig.o patched.o; do
+	in_symbols "$obj" | awk '$4 == "FILE" && $8 == "same.c"' | grep -q . || fail "missing same.c FILE"
+done
+
+run_diff
+assert_patched account
+assert_patched helper
+assert_no_klp_sym bytes
+assert_section .bss.bytes
+
+pass "private state from a different TU is new even with the same basename"
diff --git a/tools/objtool/tests/generic/test-identity-static-scope.sh b/tools/objtool/tests/generic/test-identity-static-scope.sh
new file mode 100755
index 000000000000..fc0372100886
--- /dev/null
+++ b/tools/objtool/tests/generic/test-identity-static-scope.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# Reordering functions or adding a same-named static must retain old state.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+
+build_pair identity/static_scope.c
+tx_state="$(identity_object_for orig.o tx)"
+extra_state="$(identity_object_for patched.o extra)"
+[ -n "$tx_state" ] && [ -n "$extra_state" ] || fail "missing static reference"
+
+run_diff
+assert_patched tx
+assert_not_patched rx
+assert_patched extra
+assert_section ".bss.$extra_state"
+assert_reloc_sym .text.extra "$extra_state"
+run_post_link
+assert_original_reference tx "$tx_state"
+
+build_pair identity/static_scope_reorder.c
+rx_state="$(identity_object_for orig.o rx)"
+[ -n "$rx_state" ] || fail "missing RX static reference"
+
+run_diff
+assert_patched rx
+assert_not_patched tx
+out_symbols | awk '$4 == "OBJECT" && $7 != "UND" && $8 ~ /bytes/' | grep -q . &&
+	fail "existing static state was copied"
+run_post_link
+assert_original_reference rx "$rx_state"
+
+pass "named scopes preserve state through renumbering and additions"
diff --git a/tools/objtool/tests/generic/test-identity-thinlto-name-drift.sh b/tools/objtool/tests/generic/test-identity-thinlto-name-drift.sh
new file mode 100755
index 000000000000..03bb6cf2c074
--- /dev/null
+++ b/tools/objtool/tests/generic/test-identity-thinlto-name-drift.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# A bounds check stops importing a_get(), leaving its state local.
+# With -always-rename-promoted-locals=false (used by the kernel when
+# supported), the plain promoted name moves to d.c and ELF-only matching
+# silently redirects a_get() to d.c's state. Older LLVM loses correlation
+# and copies the state instead. Both must retain a.c's state.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+clang_only "ThinLTO requires clang"
+find_thinlto_toolchain || probe_skip "no matching clang/lld pair"
+CC="$THIN_CC"
+FIXTURE_CFLAGS="$FIXTURE_CFLAGS -flto=thin -fsplit-lto-unit -fvisibility=hidden"
+
+build_one identity/promotion_bounds_a.c orig-a.o
+build_one identity/promotion_bounds_a.c patched-a.o -DPATCHED
+build_one identity/promotion_bounds_b.c b.o
+build_one identity/promotion_bounds_d.c d.o
+
+# Match the kernel's ThinLTO import limit and promotion naming policy.
+link_flags=(--lto-whole-program-visibility -mllvm -import-instr-limit=5)
+rename_flags=()
+if "$THIN_LD" -r -mllvm -always-rename-promoted-locals=false \
+	"$workdir/orig-a.o" -o "$workdir/rename-probe.o" \
+	2>"$workdir/rename-probe.log"; then
+	rename_flags=(-mllvm -always-rename-promoted-locals=false)
+fi
+for side in orig patched; do
+	"$THIN_LD" -r "${link_flags[@]}" "${rename_flags[@]}" \
+		"$workdir/$side-a.o" "$workdir/b.o" "$workdir/d.o" \
+		-o "$workdir/$side.o" 2>"$workdir/$side-link.log" ||
+		fail "ThinLTO link failed: $(tail -1 "$workdir/$side-link.log")"
+done
+
+state_a="$(identity_relocs orig.o .rela.text.a_get |
+	awk '$5 ~ /^state(\.llvm\.[0-9]+)?$/ { print $5; exit }')"
+state_d="$(identity_relocs orig.o .rela.text.d_get |
+	awk '$5 ~ /^state(\.llvm\.[0-9]+)?$/ { print $5; exit }')"
+[ -n "$state_a" ] && [ -n "$state_d" ] && [ "$state_a" != "$state_d" ] ||
+	probe_skip "ThinLTO did not emit distinct promoted state references"
+[ "$(identity_object_for patched.o a_get)" = state ] ||
+	probe_skip "the bounds check did not leave a_get's state local"
+in_symbols patched.o | awk '$4 == "OBJECT" && $5 == "LOCAL" && $8 == "state"' |
+	grep -q . || probe_skip "missing local state after the bounds check"
+
+if [ "${#rename_flags[@]}" -ne 0 ]; then
+	[ "$state_a" = state ] && [[ "$state_d" = state.llvm.* ]] ||
+		probe_skip "a_get's state did not claim the original plain name"
+	in_symbols patched.o | awk '$4 == "OBJECT" && $5 == "GLOBAL" && $8 == "state"' |
+		grep -q . || probe_skip "the plain promoted name did not move to d.c"
+fi
+
+run_diff
+assert_patched a_get
+assert_patched target
+assert_not_patched d_get
+out_symbols | awk '$4 == "OBJECT" && $7 != "UND" && $8 ~ /^state(\.|$)/' |
+	grep -q . && fail "unchanged state was allocated again"
+run_post_link
+assert_original_reference a_get "$state_a"
+assert_original_reference target "$state_d"
+
+pass "ThinLTO import changes preserve both TUs' original state"
diff --git a/tools/objtool/tests/generic/test-identity-thinlto-promotion.sh b/tools/objtool/tests/generic/test-identity-thinlto-promotion.sh
new file mode 100755
index 000000000000..62f9cc70568a
--- /dev/null
+++ b/tools/objtool/tests/generic/test-identity-thinlto-promotion.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# A new caller makes ThinLTO import rx() and promote rx.c's copy of
+# record()'s static state; the patch must keep using the original state.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+build_identity_pair thin "" identity/tx.c identity/rx.c identity/entries.c
+
+in_symbols orig.o | awk '$4 == "OBJECT" && $5 == "LOCAL" && $8 == "record.state"' |
+	grep -q . || probe_skip "RX state was not local before importing"
+[ "$(in_symbols patched.o | awk '$4 == "OBJECT" && $5 == "GLOBAL" && $8 ~ /^record.state.llvm./' | wc -l)" = 2 ] ||
+	probe_skip "ThinLTO did not promote both state instances"
+rx_state="$(identity_object_for orig.o rx)"
+[ -n "$rx_state" ] || fail "missing RX static reference"
+
+run_diff
+assert_patched entry_rx
+out_symbols | awk '$4 == "OBJECT" && $7 != "UND" && $8 ~ /record.state/' | grep -q . &&
+	fail "promoted state was allocated again"
+run_post_link
+assert_original_reference entry_rx "$rx_state"
+
+pass "header state retains its defining TU across promotion"
diff --git a/tools/objtool/tests/identity-lib.sh b/tools/objtool/tests/identity-lib.sh
new file mode 100644
index 000000000000..69d9619ae7d8
--- /dev/null
+++ b/tools/objtool/tests/identity-lib.sh
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# Shared build helpers for the multi-TU identity fixtures.
+. "$(dirname "$0")/../lib.sh"
+
+FIXTURE_CFLAGS="$FIXTURE_CFLAGS -fno-pie -fno-pic"
+
+# build_identity_pair <none|thin|full> <extra-cflags> <fixture.c...>
+build_identity_pair()
+{
+	local mode="$1" cflags="$2" side file i
+	local CC="$CC"
+	local flags=() parts=() patched=()
+	shift 2
+	if [ "$mode" != none ]; then
+		clang_only "LTO fixtures require clang"
+		find_thinlto_toolchain || probe_skip "no matching clang/lld pair"
+		flags=("-flto=$mode")
+		CC="$THIN_CC"
+	fi
+	orig_obj=orig.o
+	patched_obj=patched.o
+	for side in orig patched; do
+		patched=()
+		[ "$side" = patched ] && patched=(-DPATCHED)
+		parts=()
+		i=0
+		for file in "$@"; do
+			build_one "$file" "$side-$i.o" "${flags[@]}" "${patched[@]}" $cflags
+			parts+=("$workdir/$side-$i.o")
+			i=$((i + 1))
+		done
+		if [ "$mode" = none ]; then
+			partial_link "$workdir/$side.o" "${parts[@]}" || fail "partial link failed"
+		else
+			"$THIN_LD" -r "${parts[@]}" -o "$workdir/$side.o" || fail "LTO link failed"
+		fi
+	done
+}
+
+# identity_relocs <object> <relocation-section>
+identity_relocs()
+{
+	# readelf prints the section name in single quotes.
+	in_relocs "$1" | awk -v sec="'$2'" '
+		/Relocation section/ { active = $3 == sec }
+		active && $1 ~ /^[[:xdigit:]]+$/ { print }'
+}
+
+# The fixture's function references one static in .bss or .rodata.
+identity_object_for()
+{
+	identity_relocs "$1" ".rela.text.$2" | awk '
+		$5 ~ /^\.(bss|rodata)\./ {
+			sub(/^\.(bss|rodata)\./, "", $5); print $5; exit
+		}'
+}
+
+# After post-link, check the reference to the original symbol, not just its presence.
+assert_original_reference()
+{
+	identity_relocs out.o ".klp.rela.orig..text.$1" |
+		awk -v n=".klp.sym.orig.$2," 'index($5, n) == 1' | grep -q . ||
+		fail "expected '$1' to reference original '$2'"
+}
-- 
2.53.0


      parent reply	other threads:[~2026-10-26 10:20 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26 10:19 [RFC PATCH 0/3] objtool/klp: DWARF-based cross-build symbol correlation Longjun Luo
2026-10-26 10:19 ` [RFC PATCH 1/3] objtool/klp: Correlate symbols using DWARF identity Longjun Luo
2026-10-26 10:19 ` [RFC PATCH 2/3] objtool/klp: Include symbol identities in reference checksums Longjun Luo
2026-10-26 10:19 ` Longjun Luo [this message]

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=20261026101916.3823942-4-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®