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 02/11] objtool/klp: Hash compound literals by content
Date: Wed,  4 Nov 2026 21:31:48 +0800	[thread overview]
Message-ID: <20261104133158.3187525-3-luolongjuna@gmail.com> (raw)
In-Reply-To: <20261104133158.3187525-1-luolongjuna@gmail.com>

Compiler-generated names for compound literals do not describe their
contents. Changes to an anonymous array or a callback in an anonymous
structure can leave the referring function's checksum unchanged.

Hash compound contents and relocations recursively, using the resulting
checksum in references. Preserve target offsets and stop at named
objects. Reject cycles and nesting beyond three compound objects.

Recognize GCC and Clang compound literal names in both writable and
read-only sections. Add tests for changed array elements and callbacks.

Validated the available objtool tests with GCC 16.2.1 and Clang
22.1.8. No failures.

Assisted-by: LLM
Signed-off-by: Longjun Luo <luolongjuna@gmail.com>
---
 tools/objtool/include/objtool/klp.h           |   2 +
 tools/objtool/klp-checksum.c                  | 102 +++++++++++++-----
 tools/objtool/klp-diff.c                      |  31 ++++++
 .../generic/fixtures/identity/compound.c      |  65 +++++++++++
 .../tests/generic/test-identity-compound.sh   |  13 +++
 5 files changed, 187 insertions(+), 26 deletions(-)
 create mode 100644 tools/objtool/tests/generic/fixtures/identity/compound.c
 create mode 100755 tools/objtool/tests/generic/test-identity-compound.sh

diff --git a/tools/objtool/include/objtool/klp.h b/tools/objtool/include/objtool/klp.h
index c57775d78c71..90468e141124 100644
--- a/tools/objtool/include/objtool/klp.h
+++ b/tools/objtool/include/objtool/klp.h
@@ -51,6 +51,8 @@ struct objtool_file;
 struct elf;
 struct symbol;
 
+bool is_compound_constant(struct symbol *sym);
+
 int klp_create_symid_sections(struct objtool_file *file);
 
 int klp_sympos_init(struct elf *orig);
diff --git a/tools/objtool/klp-checksum.c b/tools/objtool/klp-checksum.c
index efaf083c03a1..2a515c7e2bf6 100644
--- a/tools/objtool/klp-checksum.c
+++ b/tools/objtool/klp-checksum.c
@@ -11,6 +11,8 @@
 #include <objtool/warn.h>
 #include <objtool/checksum.h>
 
+#define COMPOUND_MAX_DEPTH 3
+
 static int checksum_debug_init(struct objtool_file *file)
 {
 	char *dup, *s;
@@ -67,6 +69,14 @@ static bool is_anonymous_const_data(struct symbol *sym)
 	       (is_sec_sym(sym) || strstarts(sym->name, ".LC"));
 }
 
+static bool is_const_reference(struct symbol *sym)
+{
+	return is_string_sec(sym->sec) || is_anonymous_const_data(sym);
+}
+
+static int checksum_object(struct objtool_file *file, struct symbol *sym,
+			   unsigned int depth);
+
 static int checksum_update_insn(struct objtool_file *file, struct symbol *func,
 				struct instruction *insn)
 {
@@ -74,7 +84,9 @@ static int checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	struct alternative *alt;
 	unsigned long offset;
 	struct symbol *sym;
+	const void *data;
 	static bool in_alt;
+	ssize_t size;
 
 	if (insn->fake)
 		return 0;
@@ -134,10 +146,9 @@ static int checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	sym = reloc->sym;
 	offset = arch_insn_adjusted_addend(insn, reloc);
 
-	if (is_string_sec(sym->sec)) {
-		ssize_t size = elf_string_len(sym->sec, sym->offset + offset);
-		const void *data;
-
+	if (is_const_reference(sym)) {
+		size = is_string_sec(sym->sec) ? elf_string_len(sym->sec, sym->offset + offset) :
+					      sym->sec->sh.sh_entsize;
 		if (size < 0)
 			return -1;
 		data = sym->sec->data->d_buf + sym->offset + offset;
@@ -145,14 +156,6 @@ static int checksum_update_insn(struct objtool_file *file, struct symbol *func,
 		goto alts;
 	}
 
-	if (is_anonymous_const_data(sym)) {
-		void *cst;
-
-		cst = sym->sec->data->d_buf + sym->offset + offset;
-		__checksum_update_insn(func, insn, cst, sym->sec->sh.sh_entsize);
-		goto alts;
-	}
-
 	if (is_sec_sym(sym)) {
 		sym = find_symbol_containing(reloc->sym->sec, offset);
 		if (!sym)
@@ -161,8 +164,15 @@ static int checksum_update_insn(struct objtool_file *file, struct symbol *func,
 		offset -= sym->offset;
 	}
 
-	__checksum_update_insn(func, insn, sym->demangled_name,
-			       strlen(sym->demangled_name));
+	if (is_compound_constant(sym)) {
+		if (checksum_object(file, sym, 1))
+			return -1;
+		__checksum_update_insn(func, insn, &sym->csum.checksum,
+				       sizeof(sym->csum.checksum));
+	} else {
+		__checksum_update_insn(func, insn, sym->demangled_name,
+				       strlen(sym->demangled_name));
+	}
 	__checksum_update_insn(func, insn, &offset, sizeof(offset));
 
 alts:
@@ -198,7 +208,8 @@ static int checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	return 0;
 }
 
-static int checksum_update_object(struct objtool_file *file, struct symbol *sym)
+static int checksum_update_object(struct objtool_file *file, struct symbol *sym,
+				  unsigned int depth)
 {
 	struct reloc *reloc;
 
@@ -212,38 +223,79 @@ static int checksum_update_object(struct objtool_file *file, struct symbol *sym)
 	sym_for_each_reloc(file->elf, sym, reloc) {
 		unsigned long sym_offset = reloc_offset(reloc) - sym->offset;
 		struct symbol *target = reloc->sym;
+		unsigned int type = reloc_type(reloc);
+		const void *data;
+		ssize_t size;
 		s64 offset;
 
 		offset = reloc_addend(reloc);
 
-		if (is_string_sec(target->sec)) {
-			ssize_t size = elf_string_len(target->sec, target->offset + offset);
-			const void *data;
+		__checksum_update_object(sym, sym_offset, "reloc type",
+					 &type, sizeof(type));
 
+		if (is_const_reference(target)) {
+			size = is_string_sec(target->sec) ?
+				elf_string_len(target->sec, target->offset + offset) :
+				target->sec->sh.sh_entsize;
 			if (size < 0)
 				return -1;
 			data = target->sec->data->d_buf + target->offset + offset;
-			__checksum_update_object(sym, sym_offset, "reloc string", data, size);
+			__checksum_update_object(sym, sym_offset, "reloc constant", data, size);
 			continue;
 		}
 
 		if (is_sec_sym(target)) {
 			target = find_symbol_containing(reloc->sym->sec, offset);
-			if (!target)
+			if (!target) {
+				if (is_compound_constant(sym)) {
+					ERROR("%s+%#lx: unresolved compound constant reference",
+					      sym->name, sym_offset);
+					return -1;
+				}
 				continue;
+			}
 
 			offset -= target->offset;
 		}
 
-		__checksum_update_object(sym, sym_offset, "reloc name",
-					 target->demangled_name,
-					 strlen(target->demangled_name));
+		if (is_compound_constant(target)) {
+			if (checksum_object(file, target, depth + 1))
+				return -1;
+			__checksum_update_object(sym, sym_offset, "reloc compound",
+						 &target->csum.checksum,
+						 sizeof(target->csum.checksum));
+		} else {
+			__checksum_update_object(sym, sym_offset, "reloc name",
+						 target->demangled_name,
+						 strlen(target->demangled_name));
+		}
 		__checksum_update_object(sym, sym_offset, "reloc addend",
 					 &offset, sizeof(offset));
 	}
 	return 0;
 }
 
+/* Count only consecutive compound constants; named targets use their identity. */
+static int checksum_object(struct objtool_file *file, struct symbol *sym,
+			   unsigned int depth)
+{
+	if (depth > COMPOUND_MAX_DEPTH) {
+		ERROR("%s: compound constant nesting exceeds %u levels",
+		      sym->name, COMPOUND_MAX_DEPTH);
+		return -1;
+	}
+	if (sym->csum.state) {
+		ERROR("%s: cyclic compound constant reference", sym->name);
+		return -1;
+	}
+
+	checksum_init(sym);
+	if (checksum_update_object(file, sym, depth))
+		return -1;
+	checksum_finish(sym);
+	return 0;
+}
+
 int calculate_checksums(struct objtool_file *file)
 {
 	struct instruction *insn;
@@ -272,10 +324,8 @@ int calculate_checksums(struct objtool_file *file)
 			checksum_finish(sym);
 
 		} else if (is_object_sym(sym)) {
-			checksum_init(sym);
-			if (checksum_update_object(file, sym))
+			if (checksum_object(file, sym, is_compound_constant(sym) ? 1 : 0))
 				return -1;
-			checksum_finish(sym);
 		}
 
 	}
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 6321f691adbb..0391f91a8d31 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -438,6 +438,37 @@ static bool is_anonymous_rodata(struct symbol *sym)
 	       (!is_object_sym(sym) || strstarts(sym->sec->name, ".rodata.cst"));
 }
 
+/* GCC and Clang give compound literals generated names, not source identities. */
+bool is_compound_constant(struct symbol *sym)
+{
+	const char *name = sym->name;
+
+	if (!is_object_sym(sym) || is_undef_sym(sym) || !sym->len ||
+	    !sym->sec)
+		return false;
+
+	if (strstarts(name, ".compoundliteral"))
+		name += strlen(".compoundliteral");
+	else if (strstarts(name, "__compound_literal"))
+		name += strlen("__compound_literal");
+	else
+		return false;
+
+	while (*name == '.' && isdigit((unsigned char)name[1])) {
+		name++;
+		while (isdigit((unsigned char)*name))
+			name++;
+	}
+	if (strstarts(name, ".llvm.")) {
+		name += strlen(".llvm.");
+		if (!isdigit((unsigned char)*name))
+			return false;
+		while (isdigit((unsigned char)*name))
+			name++;
+	}
+	return !*name;
+}
+
 /*
  * These symbols should never be correlated, so their local patched versions
  * are used instead of linking to the originals.
diff --git a/tools/objtool/tests/generic/fixtures/identity/compound.c b/tools/objtool/tests/generic/fixtures/identity/compound.c
new file mode 100644
index 000000000000..bf607bdd6093
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/identity/compound.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com> */
+#include "common.h"
+MODINFO;
+extern void observe(int value);
+
+NOIPA int read_value(int value)
+{
+	return value + 1;
+}
+
+NOIPA int other_value(int value)
+{
+	return value + 2;
+}
+
+#ifdef WRITABLE
+#define CONST
+#else
+#define CONST const
+#endif
+
+struct config {
+	CONST unsigned int *values;
+	const char *label;
+	int (*read)(int value);
+};
+
+#if defined(PATCHED) && defined(CHANGE_LEAF)
+#define FIRST 12
+#else
+#define FIRST 11
+#endif
+#if defined(PATCHED) && defined(CHANGE_TARGET)
+#define READ other_value
+#else
+#define READ read_value
+#endif
+
+static CONST struct config *const first = &(CONST struct config) {
+	(CONST unsigned int[]) { FIRST, 22, 33, 44, 55, 66, 77, 88 }, "table", READ
+};
+static CONST struct config *const second = &(CONST struct config) {
+	(CONST unsigned int[]) { 11, 22, 33, 44, 55, 66, 77, 88 }, "table", read_value
+};
+
+NOIPA CONST struct config *target(int value)
+{
+#if defined(PATCHED) && defined(CHANGE_FUNCTION)
+	if (value < 0)
+		return (void *)0;
+#endif
+	observe(value);
+#ifdef AMBIGUOUS
+	return value ? first : second;
+#else
+	return first;
+#endif
+}
+
+NOIPA CONST struct config *unchanged(int value)
+{
+	observe(value);
+	return second;
+}
diff --git a/tools/objtool/tests/generic/test-identity-compound.sh b/tools/objtool/tests/generic/test-identity-compound.sh
new file mode 100755
index 000000000000..76b16616b32c
--- /dev/null
+++ b/tools/objtool/tests/generic/test-identity-compound.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Longjun Luo <luolongjuna@gmail.com>
+# Array contents and callbacks contribute to the referring function's checksum.
+. "$(dirname "$0")/../identity-lib.sh"
+
+setup
+for change in CHANGE_LEAF CHANGE_TARGET; do
+	build_pair identity/compound.c "-D$change"
+	assert_checksum_differs target
+done
+
+pass "compound contents contribute to reference checksums"
-- 
2.55.0


  parent reply	other threads:[~2026-11-04 13:32 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 ` Longjun Luo [this message]
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 ` [RFC PATCH v2 09/11] objtool/klp: Extend twins through unchanged function references Longjun Luo
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-3-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®