Thread (82 messages) flat view 82 messages, 2 authors, 3d ago
WARM3d

Revision v3 of 5 in this series.

Revisions (5)
  1. v1 [diff vs current]
  2. v2 [diff vs current]
  3. v3 current
  4. v4 [diff vs current]
  5. v5 [diff vs current]

[PATCH v3 38/58] objtool/klp: Add test for sympos resolved against a linked vmlinux

From: Song Liu <song@kernel.org>
Date: 2026-09-14 23:07:08
Subsystem: objtool, the rest · Maintainers: Josh Poimboeuf, Peter Zijlstra, Linus Torvalds

vmlinux is not like a module: the final link reorders sub-sections, so a
symbol's position has to come from the linked image rather than from symbol
table order.  klp diff bridges that with .klp.symid, and looks for it only
when the object it was handed is called vmlinux.o with a vmlinux beside it.

This was assigned to an end-to-end test on the assumption that it needs a
real kernel build.  It needs "ld -r" and "ld -e 0", and takes a fraction of
a second.

The fixture places the static appearing first in the symbol table at the
higher address, and the link passes --sort-section=name to force the
reordering the kernel's linker script performs.  Without that the two ways
of computing sympos agree, and a first version passed with the vmlinux path
disabled.

Assisted-by: Claude:claude-opus-4
Based-on-test-by: Joe Lawrence [off-list ref]
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
 .../tests/generic/fixtures/sympos_vmlinux.c   | 40 +++++++++++++
 .../tests/generic/test-sympos-vmlinux.sh      | 57 +++++++++++++++++++
 2 files changed, 97 insertions(+)
 create mode 100644 tools/objtool/tests/generic/fixtures/sympos_vmlinux.c
 create mode 100755 tools/objtool/tests/generic/test-sympos-vmlinux.sh
diff --git a/tools/objtool/tests/generic/fixtures/sympos_vmlinux.c b/tools/objtool/tests/generic/fixtures/sympos_vmlinux.c
new file mode 100644
index 000000000000..d5e70994c582
--- /dev/null
+++ b/tools/objtool/tests/generic/fixtures/sympos_vmlinux.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Two translation units with a same-named static, placed so that the linker
+ * puts them in the opposite order to the one they appear in the symbol table.
+ *
+ * VARSEC selects the section the static lands in.  Linking with
+ * --sort-section=name then orders them alphabetically rather than by object
+ * order, so the first symbol in the symbol table ends up at the *higher*
+ * address.  That is the whole point: counting symbol table order and reading
+ * the linked image's addresses now give different answers, which is what makes
+ * it possible to tell which one klp diff used.
+ *
+ * Only use_a is patched, so exactly one sympos is emitted and there is nothing
+ * to attribute.
+ */
+
+#ifndef FUNC_NAME
+#define FUNC_NAME use_a
+#endif
+#ifndef VARSEC
+#define VARSEC ".data.mmm"
+#endif
+
+#ifndef NO_MODINFO
+static const char __modinfo[]
+	__attribute__((section(".modinfo"), used, aligned(1))) = "\0name=vmlinux";
+#endif
+
+/* volatile so it survives as an STT_OBJECT rather than being folded away */
+static volatile int dup_counter __attribute__((section(VARSEC))) = 1;
+
+int FUNC_NAME(int x)
+{
+	dup_counter += x;
+#ifdef PATCHED
+	return dup_counter + 1;
+#else
+	return dup_counter;
+#endif
+}
diff --git a/tools/objtool/tests/generic/test-sympos-vmlinux.sh b/tools/objtool/tests/generic/test-sympos-vmlinux.sh
new file mode 100755
index 000000000000..b2b44001446f
--- /dev/null
+++ b/tools/objtool/tests/generic/test-sympos-vmlinux.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# sympos for vmlinux, which is resolved differently from sympos for a module.
+#
+# A module's .ko preserves symbol table order, so klp diff can count -- that is
+# what test-sympos covers.  vmlinux cannot be counted: the final link reorders
+# sub-sections, so the order in vmlinux.o is not the order the running kernel
+# has.  klp diff bridges that with .klp.symid, a table of { id, address }
+# emitted into vmlinux.o whose addresses the linker resolves, read back out of
+# the linked vmlinux.
+#
+# Getting it wrong points the relocation at a different symbol of the same
+# name.  Nothing fails to build or load; the patched code uses the wrong
+# object.
+#
+# The fixture is arranged so the two answers differ: the static that comes
+# first in the symbol table is placed at the *higher* address, so counting
+# gives 1 and reading the linked image gives 2.  Without that, both paths agree
+# and the test cannot tell them apart.
+
+. "$(dirname "$0")/../lib.sh"
+
+setup
+
+# use_a's static sorts last by section name, use_b's first.  Only use_a is
+# patched, so exactly one sympos comes out.
+build_one sympos_vmlinux.c orig_a.o    -DFUNC_NAME=use_a -DVARSEC='".data.zzz"'
+build_one sympos_vmlinux.c patched_a.o -DFUNC_NAME=use_a -DVARSEC='".data.zzz"' -DPATCHED
+build_one sympos_vmlinux.c b.o         -DFUNC_NAME=use_b -DVARSEC='".data.aaa"' -DNO_MODINFO
+
+make_vmlinux_pair "$workdir/orig_a.o" "$workdir/b.o" \
+	       -- "$workdir/patched_a.o" "$workdir/b.o"
+
+[ "$(count_input_symbols vmlinux.o dup_counter)" = 2 ] ||
+	fail "fixture did not produce two dup_counter symbols"
+has_input_section vmlinux.o .klp.symid ||
+	fail "objtool --klp-symids emitted no .klp.symid table"
+has_input_section vmlinux .klp.symid ||
+	fail ".klp.symid did not survive the link"
+
+# The premise: symbol table order and address order must disagree, or the test
+# proves nothing.
+first_addr="$(in_symbols vmlinux | awk '$8 == "dup_counter" { print $2; exit }')"
+low_addr="$(in_symbols vmlinux | awk '$8 == "dup_counter" { print $2 }' | sort | head -1)"
+[ "$first_addr" != "$low_addr" ] ||
+	probe_skip "linker did not reorder the two statics"
+
+assert_input_symbol dup_counter
+run_diff
+
+# Address order says 2.  Counting symbol table order would say 1.
+assert_klp_sympos dup_counter 2
+out_symbols | grep -q 'dup_counter,1' &&
+	fail "sympos 1 emitted: counted symbol table order instead of reading the linked vmlinux"
+
+pass "vmlinux sympos taken from the linked image, not from symbol table order"
-- 
2.53.0-Meta
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help