xref: /linux/tools/objtool/tests/lib.sh (revision 2ab0faeb71936be1f53a8222cd5d0a1bcada5b82)
1cff27d90SPuranjay Mohan# SPDX-License-Identifier: GPL-2.0
2cff27d90SPuranjay Mohan#
3cff27d90SPuranjay Mohan# Helpers for the objtool klp tests.  A test builds a fixture twice, as the
4cff27d90SPuranjay Mohan# original and (with -DPATCHED) the patched object, runs both through
5cff27d90SPuranjay Mohan# "klp checksum" and diffs them, then asserts on the result.
6cff27d90SPuranjay Mohan#
7cff27d90SPuranjay Mohan# Assertions check properties rather than compare against recorded output:
8cff27d90SPuranjay Mohan# codegen varies between compilers and golden files would report churn instead
9cff27d90SPuranjay Mohan# of regressions.
10cff27d90SPuranjay Mohan
11cff27d90SPuranjay MohanTESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12d76312b8SSong Liu
13d76312b8SSong Liu# Tests live in generic/ or in an architecture directory beside it, and each
14d76312b8SSong Liu# carries its own fixtures.
15d76312b8SSong LiuFIXTURES_DIR="$(cd "$(dirname "$0")/fixtures" 2>/dev/null && pwd)"
16cff27d90SPuranjay Mohan
17c009fabcSSong Liu# The kernel's convention: CROSS_COMPILE is the one knob, with per-tool
18c009fabcSSong Liu# overrides for what it does not cover.  objtool itself is always a host binary
19c009fabcSSong Liu# -- it is built with HOSTCC and only reads ELF -- so an arm64 machine can run
20c009fabcSSong Liu# the x86 tests against x86 objects given a compiler that emits them.
21c009fabcSSong Liu#
22c009fabcSSong Liu# readelf reads any target, so it rarely needs overriding, and either GNU
23c009fabcSSong Liu# readelf or llvm-readelf will do: the assertions match on fields rather than
24c009fabcSSong Liu# on columns, and where the two spell something differently -- "OS [0xff20]"
25c009fabcSSong Liu# against "OS[0xff20]" for SHN_LIVEPATCH -- they accept both.  BFD's objcopy is
26c009fabcSSong Liu# usually built for the host's target alone, and llvm-objcopy is the
27c009fabcSSong Liu# target-agnostic replacement.
28c009fabcSSong LiuCROSS_COMPILE="${CROSS_COMPILE:-}"
29c009fabcSSong LiuCC="${CC:-${CROSS_COMPILE}gcc}"
30c009fabcSSong LiuLD="${LD:-${CROSS_COMPILE}ld}"
31c009fabcSSong LiuREADELF="${READELF:-${CROSS_COMPILE}readelf}"
32c009fabcSSong LiuOBJCOPY="${OBJCOPY:-${CROSS_COMPILE}objcopy}"
33c009fabcSSong Liu
34cff27d90SPuranjay MohanOBJTOOL="${OBJTOOL:-$TESTS_DIR/../objtool}"
35c009fabcSSong Liu
36c009fabcSSong Liu# klp_preflight
37c009fabcSSong Liu#
38c009fabcSSong Liu# Check the environment once, before any test runs, and report what was found.
39c009fabcSSong Liu#
40c009fabcSSong Liuklp_preflight()
41c009fabcSSong Liu{
42d76312b8SSong Liu	local tmp tool cc_version arch host cc_arch
43c009fabcSSong Liu
44c009fabcSSong Liu	bail() { echo "Bail out! $*" >&2; exit 1; }
45c009fabcSSong Liu
46c009fabcSSong Liu	# A relative $OBJTOOL is relative to the objtool directory, not tests/.
47c009fabcSSong Liu	[ -x "$OBJTOOL" ] || [ ! -x "$TESTS_DIR/../$OBJTOOL" ] ||
48c009fabcSSong Liu		OBJTOOL="$TESTS_DIR/../$OBJTOOL"
49c009fabcSSong Liu
50c009fabcSSong Liu	[ -x "$OBJTOOL" ] ||
51c009fabcSSong Liu		bail "objtool not found at '$OBJTOOL' -- build it first"
52c009fabcSSong Liu
53c009fabcSSong Liu	# run_diff() runs objtool from inside the test's working directory, so
54c009fabcSSong Liu	# a relative path would resolve against that instead.
55c009fabcSSong Liu	OBJTOOL="$(realpath "$OBJTOOL")"
56c009fabcSSong Liu
57c009fabcSSong Liu	"$OBJTOOL" klp 2>&1 | grep -q checksum ||
58c009fabcSSong Liu		bail "objtool was built without klp support; install libxxhash (>= 0.8) and rebuild"
59c009fabcSSong Liu
60c009fabcSSong Liu	command -v "${CC%% *}" >/dev/null || bail "compiler not found: $CC"
61c009fabcSSong Liu
62c009fabcSSong Liu	for tool in "$READELF" "$OBJCOPY" "$LD"; do
63c009fabcSSong Liu		command -v "${tool%% *}" >/dev/null || bail "$tool not found"
64c009fabcSSong Liu	done
65c009fabcSSong Liu
66c009fabcSSong Liu	tmp="$(mktemp -d)" || bail "mktemp failed"
67c009fabcSSong Liu	echo 'int probe(void) { return 0; }' > "$tmp/probe.c"
68c009fabcSSong Liu	$CC -c -o "$tmp/probe.o" "$tmp/probe.c" 2>/dev/null ||
69c009fabcSSong Liu		{ rm -rf "$tmp"; bail "$CC cannot compile a trivial object"; }
70c009fabcSSong Liu
71c009fabcSSong Liu	# $CC, $ARCH and objtool have to agree about the target, and cross runs
72c009fabcSSong Liu	# are where they stop agreeing: plain "CC=clang ARCH=x86_64" on an arm64
73c009fabcSSong Liu	# box selects the x86 tests and then builds arm64 objects, because clang
74c009fabcSSong Liu	# needs --target= to emit anything but the host's.
75c009fabcSSong Liu	#
76c009fabcSSong Liu	# Ask objtool rather than comparing machine names.  It rejects an object
77c009fabcSSong Liu	# it was not built for -- "unexpected ELF machine type" -- so one check
78c009fabcSSong Liu	# covers every way the three can disagree, and says so once instead of
79c009fabcSSong Liu	# failing every test for the same reason.
80c009fabcSSong Liu	"$OBJTOOL" klp checksum "$tmp/probe.o" >/dev/null 2>&1 ||
81c009fabcSSong Liu		{ rm -rf "$tmp"
82c009fabcSSong Liu		  bail "objtool rejects an object built by '$CC'; they target" \
83c009fabcSSong Liu		       "different architectures (set CROSS_COMPILE, or" \
84c009fabcSSong Liu		       "--target= for clang)"; }
85c009fabcSSong Liu
86c009fabcSSong Liu	# BFD objcopy is usually built for the host's target alone, and
87c009fabcSSong Liu	# checksum_of() needs it to read the object under test.
88c009fabcSSong Liu	$OBJCOPY -O binary --only-section=.text "$tmp/probe.o" "$tmp/probe.bin" 2>/dev/null ||
89c009fabcSSong Liu		{ rm -rf "$tmp"
90c009fabcSSong Liu		  bail "$OBJCOPY cannot read objects built by '$CC'; install" \
91c009fabcSSong Liu		       "binutils-multiarch or set OBJCOPY=llvm-objcopy"; }
92c009fabcSSong Liu	# $ARCH only chooses which directory of tests runs, so it can disagree
93c009fabcSSong Liu	# with what $CC builds without objtool noticing -- and the result is the
94c009fabcSSong Liu	# wrong set of tests, quietly.
95c009fabcSSong Liu	case "$($READELF -hW "$tmp/probe.o" | sed -n 's/.*Machine: *//p')" in
96c009fabcSSong Liu	*X86-64*|*Intel*80386*)	cc_arch=x86 ;;
97c009fabcSSong Liu	*AArch64*)		cc_arch=arm64 ;;
98c009fabcSSong Liu	*)			cc_arch= ;;
99c009fabcSSong Liu	esac
100c009fabcSSong Liu	rm -rf "$tmp"
101c009fabcSSong Liu
102d76312b8SSong Liu	# Normalize to the kernel's SRCARCH.
103d76312b8SSong Liu	case "${ARCH:-$(uname -m)}" in
104d76312b8SSong Liu	x86_64|i?86)	arch=x86 ;;
105d76312b8SSong Liu	aarch64*)	arch=arm64 ;;
106d76312b8SSong Liu	*)		arch="${ARCH:-$(uname -m)}" ;;
107d76312b8SSong Liu	esac
108d76312b8SSong Liu
109d76312b8SSong Liu	case "$(uname -m)" in
110d76312b8SSong Liu	x86_64|i?86)	host=x86 ;;
111d76312b8SSong Liu	aarch64*)	host=arm64 ;;
112d76312b8SSong Liu	*)		host="$(uname -m)" ;;
113d76312b8SSong Liu	esac
114d76312b8SSong Liu
115d76312b8SSong Liu	[ -z "$cc_arch" ] || [ "$cc_arch" = "$arch" ] ||
116d76312b8SSong Liu		bail "ARCH says $arch but '$CC' builds $cc_arch objects;" \
117d76312b8SSong Liu		     "the $arch tests would run against the wrong architecture"
118d76312b8SSong Liu
119d76312b8SSong Liu	KLP_TEST_ARCH="$arch"
120c009fabcSSong Liu	KLP_TEST_PREFLIGHT=done
121d76312b8SSong Liu	export OBJTOOL CC KLP_TEST_ARCH KLP_TEST_PREFLIGHT
122c009fabcSSong Liu
123c009fabcSSong Liu	cc_version="$($CC --version 2>/dev/null | head -1)"
124c009fabcSSong Liu	cat <<EOF
125c009fabcSSong Liu# preflight
126c009fabcSSong Liu#   objtool   $OBJTOOL (klp: yes)
127c009fabcSSong Liu#   compiler  $cc_version
128c009fabcSSong Liu#   arch      $KLP_TEST_ARCH$([ "$arch" = "$host" ] || echo "  (host $host, cross)")
129c009fabcSSong LiuEOF
130c009fabcSSong Liu}
131c009fabcSSong Liu
132c009fabcSSong Liu[ -n "${KLP_TEST_PREFLIGHT:-}" ] || klp_preflight
133cff27d90SPuranjay Mohan
134ae04b6d0SSong Liu# What every fixture is built with.  These describe the kernel a fixture stands
135ae04b6d0SSong Liu# in for; -c is build_one's contract rather than a property of that kernel, so
136ae04b6d0SSong Liu# it lives at the compile where an override cannot drop it.
137ae04b6d0SSong Liu#
138ae04b6d0SSong Liu#   -O2					the kernel's default
139ae04b6d0SSong Liu#   -ffunction-sections -fdata-sections	klp-build passes these itself, through
140ae04b6d0SSong Liu#					KCFLAGS, whatever the configuration
141ae04b6d0SSong Liu#   -fno-asynchronous-unwind-tables	arch/x86/Makefile sets this always, so
142ae04b6d0SSong Liu#					kernel objects carry no .eh_frame
143ae04b6d0SSong Liu#   -fno-common				the kernel's Makefile sets it, so an
144ae04b6d0SSong Liu#					uninitialised global there lands in
145ae04b6d0SSong Liu#					.bss rather than being SHN_COMMON,
146ae04b6d0SSong Liu#					which has no section and so no
147ae04b6d0SSong Liu#					checksum
148ae04b6d0SSong Liu#
149ae04b6d0SSong Liu# A test overrides it; see tools/objtool/Documentation/klp-write-tests.txt.
150ae04b6d0SSong LiuFIXTURE_CFLAGS="-O2 -ffunction-sections -fdata-sections -fno-common \
151ae04b6d0SSong Liu		-fno-asynchronous-unwind-tables"
152cff27d90SPuranjay Mohan
153cff27d90SPuranjay Mohantest_name="$(basename "$0" .sh)"
154cff27d90SPuranjay Mohanworkdir=
155cff27d90SPuranjay Mohan
156ae04b6d0SSong Liu# The pair run_diff() and the checksum helpers work on.  build_pair() names
157ae04b6d0SSong Liu# them again and make_vmlinux_pair() repoints orig_obj at the image it links,
158ae04b6d0SSong Liu# but a test which builds its objects itself with build_one() sets neither, so
159ae04b6d0SSong Liu# the default belongs here.
160ae04b6d0SSong Liuorig_obj=orig.o
161ae04b6d0SSong Liupatched_obj=patched.o
162ae04b6d0SSong Liu
163cff27d90SPuranjay Mohanpass() { echo "ok - $test_name${1:+: $1}"; exit 0; }
164317cec90SSong Liufail() { echo "not ok - $test_name: $1"; exit 1; }
165317cec90SSong Liu
166317cec90SSong Liu# Two kinds of skip, and the runner tells them apart.
167317cec90SSong Liu#
168317cec90SSong Liu#   declared_skip  the test said in advance it does not apply here, e.g.
169317cec90SSong Liu#                  gcc_only on a clang run.  Expected indefinitely.
170317cec90SSong Liu#   probe_skip     the construct did not turn up in the built object this
171317cec90SSong Liu#                  time.  Weaker: it may appear on another compiler version,
172317cec90SSong Liu#                  and one which becomes permanent is a fixture that quietly
173317cec90SSong Liu#                  stopped testing anything.
174317cec90SSong Liu#
175317cec90SSong Liu# A bare skip() is neither, and the runner counts it as a failure: a test which
176317cec90SSong Liu# gives up for a reason it never declared is a hole, not an outcome.
177317cec90SSong Liudeclared_skip() { echo "ok - $test_name # SKIP (declared) $*"; exit 0; }
178317cec90SSong Liuprobe_skip()    { echo "ok - $test_name # SKIP (probe) $*"; exit 0; }
179317cec90SSong Liuskip()          { echo "ok - $test_name # SKIP $*"; exit 0; }
180317cec90SSong Liu
181317cec90SSong Liu# TAP directives.  A test which is known to fail reports it rather than being
182317cec90SSong Liu# commented out and forgotten, and one which starts passing again says so
183317cec90SSong Liu# instead of quietly going green: the expectation has to be removed by hand,
184317cec90SSong Liu# which is the point.
185317cec90SSong Liuxfail()
186317cec90SSong Liu{
187317cec90SSong Liu	echo "not ok - $test_name${1:+: $1} # TODO known failure"
188317cec90SSong Liu	exit 0
189317cec90SSong Liu}
190317cec90SSong Liu
191317cec90SSong Liuxpass()
192317cec90SSong Liu{
193317cec90SSong Liu	echo "ok - $test_name${1:+: $1} # TODO expected failure, but passed"
194317cec90SSong Liu	exit 1
195317cec90SSong Liu}
196cff27d90SPuranjay Mohan
197cff27d90SPuranjay Mohancleanup() { [ -n "$workdir" ] && rm -rf "$workdir"; }
198cff27d90SPuranjay Mohan
199cff27d90SPuranjay Mohan# setup [exported symbol...]
200cff27d90SPuranjay Mohansetup()
201cff27d90SPuranjay Mohan{
202c009fabcSSong Liu	# The environment was checked once when this file was sourced, so there
203c009fabcSSong Liu	# is nothing to verify here: objtool exists at the resolved path, has
204c009fabcSSong Liu	# klp support, and $CC works.
205cff27d90SPuranjay Mohan	workdir="$(mktemp -d)" || fail "mktemp failed"
206cff27d90SPuranjay Mohan	trap cleanup EXIT
207cff27d90SPuranjay Mohan
208cff27d90SPuranjay Mohan	export_syms "$@"
209cff27d90SPuranjay Mohan}
210cff27d90SPuranjay Mohan
211cff27d90SPuranjay Mohan# export_syms [symbol...]
212cff27d90SPuranjay Mohan#
213cff27d90SPuranjay Mohan# Rewrite Module.symvers so exactly these symbols are exported by vmlinux.
214cff27d90SPuranjay Mohan# Whether a symbol is listed decides between an ordinary relocation and a klp
215cff27d90SPuranjay Mohan# relocation, so tests flip it to cover both.
216cff27d90SPuranjay Mohanexport_syms()
217cff27d90SPuranjay Mohan{
218cff27d90SPuranjay Mohan	: > "$workdir/Module.symvers"
219*2ab0faebSSong Liu	add_exports vmlinux "$@"
220*2ab0faebSSong Liu}
221*2ab0faebSSong Liu
222*2ab0faebSSong Liu# add_exports <object> [symbol...]
223*2ab0faebSSong Liu#
224*2ab0faebSSong Liu# Append exports owned by one object, without clearing what is already there,
225*2ab0faebSSong Liu# so a test can describe a kernel where several objects export things.
226*2ab0faebSSong Liu#
227*2ab0faebSSong Liu# Which object owns a symbol is not cosmetic: a reference to a vmlinux symbol
228*2ab0faebSSong Liu# is applied when the patch module loads, and a reference to a module's symbol
229*2ab0faebSSong Liu# when that patched module loads, so klp diff files them in different sections.
230*2ab0faebSSong Liuadd_exports()
231*2ab0faebSSong Liu{
232*2ab0faebSSong Liu	local owner="$1"; shift
233*2ab0faebSSong Liu
234*2ab0faebSSong Liu	add_exports_ns "$owner" "" "$@"
235*2ab0faebSSong Liu}
236*2ab0faebSSong Liu
237*2ab0faebSSong Liu# add_exports_ns <object> <namespace> [symbol...]
238*2ab0faebSSong Liu#
239*2ab0faebSSong Liu# Exports in a symbol namespace, the last field of a Module.symvers line.
240*2ab0faebSSong Liu#
241*2ab0faebSSong Liu# A "module:<names>" namespace is EXPORT_SYMBOL_FOR_MODULES(), where the module
242*2ab0faebSSong Liu# loader grants access by matching the importing module's name against the
243*2ab0faebSSong Liu# list.  A livepatch module is never on that list, so such a symbol has to be
244*2ab0faebSSong Liu# referenced the way an unexported one is.  Ordinary namespaces are not
245*2ab0faebSSong Liu# special here.
246*2ab0faebSSong Liuadd_exports_ns()
247*2ab0faebSSong Liu{
248*2ab0faebSSong Liu	local owner="$1" ns="$2"; shift 2
249*2ab0faebSSong Liu
250*2ab0faebSSong Liu	local sym
251*2ab0faebSSong Liu
252cff27d90SPuranjay Mohan	for sym in "$@"; do
253*2ab0faebSSong Liu		printf '0x00000000\t%s\t%s\tEXPORT_SYMBOL\t%s\n' \
254*2ab0faebSSong Liu			"$sym" "$owner" "$ns" >> "$workdir/Module.symvers"
255cff27d90SPuranjay Mohan	done
256cff27d90SPuranjay Mohan}
257cff27d90SPuranjay Mohan
258317cec90SSong Liu# gcc_only / clang_only <reason>
259317cec90SSong Liugcc_only()
260317cec90SSong Liu{
261317cec90SSong Liu	case "$($CC --version 2>/dev/null | head -1)" in
262317cec90SSong Liu	*[Gg][Cc][Cc]*)	return 0 ;;
263317cec90SSong Liu	esac
264317cec90SSong Liu	declared_skip "gcc only${1:+: $1}"
265317cec90SSong Liu}
266317cec90SSong Liu
267317cec90SSong Liuclang_only()
268317cec90SSong Liu{
269317cec90SSong Liu	case "$($CC --version 2>/dev/null | head -1)" in
270317cec90SSong Liu	*clang*)	return 0 ;;
271317cec90SSong Liu	esac
272317cec90SSong Liu	declared_skip "clang only${1:+: $1}"
273317cec90SSong Liu}
274317cec90SSong Liu
275ae04b6d0SSong Liu# build_one <fixture.c> <output object> [cflags...]
276ae04b6d0SSong Liubuild_one()
277cff27d90SPuranjay Mohan{
278ae04b6d0SSong Liu	local fixture out
279ae04b6d0SSong Liu	fixture="$FIXTURES_DIR/$1"
280ae04b6d0SSong Liu	out="$workdir/$2"
281ae04b6d0SSong Liu	shift 2
282cff27d90SPuranjay Mohan
283cff27d90SPuranjay Mohan	[ -f "$fixture" ] || fail "missing fixture $fixture"
284cff27d90SPuranjay Mohan
285ae04b6d0SSong Liu	# run_checksum only runs once per workdir.  A fresh object has no
286ae04b6d0SSong Liu	# checksums in it, so anything built now needs that to happen again.
287ae04b6d0SSong Liu	rm -f "$workdir/.checksummed"
288ae04b6d0SSong Liu
289ae04b6d0SSong Liu	$CC -c $FIXTURE_CFLAGS "$@" -o "$out" "$fixture" 2>"$workdir/cc.log" ||
290ae04b6d0SSong Liu		fail "$(basename "$fixture") does not build: $(tail -1 "$workdir/cc.log")"
291ae04b6d0SSong Liu}
292ae04b6d0SSong Liu
293ae04b6d0SSong Liu# build_pair <fixture.c> [cflags...]
294ae04b6d0SSong Liubuild_pair()
295ae04b6d0SSong Liu{
296ae04b6d0SSong Liu	local fixture="$1"; shift
297ae04b6d0SSong Liu
298ae04b6d0SSong Liu	# Name what this builds.  A test may run several segments, and
299ae04b6d0SSong Liu	# make_vmlinux_pair() repoints orig_obj at the image it links, so
300ae04b6d0SSong Liu	# without this the next run_diff() would still be reading that.
301ae04b6d0SSong Liu	orig_obj=orig.o
302ae04b6d0SSong Liu	patched_obj=patched.o
303ae04b6d0SSong Liu
304ae04b6d0SSong Liu	build_one "$fixture" orig.o "$@"
305ae04b6d0SSong Liu	build_one "$fixture" patched.o "$@" -DPATCHED
306ae04b6d0SSong Liu}
307ae04b6d0SSong Liu
308ae04b6d0SSong Liu# run_objtool_check <objtool arguments...>
309ae04b6d0SSong Liu#
310ae04b6d0SSong Liu# Run objtool's ordinary check pass over the pair, as the kernel build does.
311ae04b6d0SSong Liu#
312ae04b6d0SSong Liu# Some of what klp diff consumes is produced by this pass rather than by the
313ae04b6d0SSong Liu# compiler: .static_call_sites, .mcount_loc, .ibt_endbr_seal, ORC.
314ae04b6d0SSong Liu#
315ae04b6d0SSong Liu# Only module objects see it before klp-build -- with CONFIG_KLP_BUILD the
316ae04b6d0SSong Liu# per-object pass is deferred, so built-in objects reach klp diff exactly as
317ae04b6d0SSong Liu# the compiler left them.
318ae04b6d0SSong Liurun_objtool_check()
319ae04b6d0SSong Liu{
320ae04b6d0SSong Liu	local obj
321ae04b6d0SSong Liu
322ae04b6d0SSong Liu	# This rewrites both objects, so checksums taken before it describe
323ae04b6d0SSong Liu	# something that no longer exists.  As in build_one(), drop the marker
324ae04b6d0SSong Liu	# so run_checksum() takes them again.
325ae04b6d0SSong Liu	rm -f "$workdir/.checksummed"
326ae04b6d0SSong Liu
327ae04b6d0SSong Liu	for obj in "$orig_obj" "$patched_obj"; do
328ae04b6d0SSong Liu		"$OBJTOOL" "$@" "$workdir/$obj" ||
329ae04b6d0SSong Liu			fail "objtool $* failed on $obj"
330ae04b6d0SSong Liu	done
331ae04b6d0SSong Liu}
332ae04b6d0SSong Liu
333ae04b6d0SSong Liurun_checksum()
334ae04b6d0SSong Liu{
335ae04b6d0SSong Liu	# Checksums live in the objects, and a test may ask for them more than
336ae04b6d0SSong Liu	# once -- diffing the same pair again with a different Module.symvers,
337ae04b6d0SSong Liu	# say.  objtool does the right thing when asked twice, leaving the
338ae04b6d0SSong Liu	# object alone, but it says so, and that warning would be most of what
339ae04b6d0SSong Liu	# a passing run prints.  Remember instead, and keep quiet.
340ae04b6d0SSong Liu	[ -e "$workdir/.checksummed" ] && return 0
341ae04b6d0SSong Liu
342ae04b6d0SSong Liu	"$OBJTOOL" klp checksum "$workdir/$orig_obj" ||
343ae04b6d0SSong Liu		fail "klp checksum $orig_obj failed"
344ae04b6d0SSong Liu	"$OBJTOOL" klp checksum "$workdir/$patched_obj" ||
345ae04b6d0SSong Liu		fail "klp checksum $patched_obj failed"
346ae04b6d0SSong Liu	touch "$workdir/.checksummed"
347cff27d90SPuranjay Mohan}
348cff27d90SPuranjay Mohan
349cff27d90SPuranjay Mohan# run_diff [expected exit status]
350cff27d90SPuranjay Mohanrun_diff()
351cff27d90SPuranjay Mohan{
352cff27d90SPuranjay Mohan	local expect="${1:-0}" rc=0
353cff27d90SPuranjay Mohan
354ae04b6d0SSong Liu	run_checksum
355cff27d90SPuranjay Mohan
356cff27d90SPuranjay Mohan	# klp diff looks for Module.symvers relative to the working directory.
357ae04b6d0SSong Liu	( cd "$workdir" && "$OBJTOOL" klp diff "$orig_obj" "$patched_obj" out.o ) \
358cff27d90SPuranjay Mohan		> "$workdir/diff.log" 2>&1 || rc=$?
359cff27d90SPuranjay Mohan
360cff27d90SPuranjay Mohan	[ "$rc" = "$expect" ] ||
361cff27d90SPuranjay Mohan		fail "klp diff exited $rc, expected $expect: $(tail -2 "$workdir/diff.log")"
362cff27d90SPuranjay Mohan}
363cff27d90SPuranjay Mohan
364cff27d90SPuranjay Mohancc_supports()
365cff27d90SPuranjay Mohan{
366cff27d90SPuranjay Mohan	echo 'int f(void) { return 0; }' > "$workdir/flagtest.c"
367cff27d90SPuranjay Mohan	$CC $1 -c "$workdir/flagtest.c" -o "$workdir/flagtest.o" 2>/dev/null
368cff27d90SPuranjay Mohan}
369cff27d90SPuranjay Mohan
370cff27d90SPuranjay Mohan# partial_link <output> <object...>
371cff27d90SPuranjay Mohan#
372cff27d90SPuranjay Mohan# "ld -r" through the compiler driver so the link targets the same
373cff27d90SPuranjay Mohan# architecture as the objects.
374cff27d90SPuranjay Mohanpartial_link()
375cff27d90SPuranjay Mohan{
376cff27d90SPuranjay Mohan	local out="$1"; shift
377cff27d90SPuranjay Mohan
378ae04b6d0SSong Liu	rm -f "$workdir/.checksummed"
379ae04b6d0SSong Liu
380cff27d90SPuranjay Mohan	$CC -r -nostdlib -o "$out" "$@" 2>/dev/null ||
381cff27d90SPuranjay Mohan		$CC -r -nostdlib -fuse-ld=lld -o "$out" "$@" 2>/dev/null
382cff27d90SPuranjay Mohan}
383cff27d90SPuranjay Mohan
384ae04b6d0SSong Liu# link_vmlinux <output> <object...>
385ae04b6d0SSong Liu#
386ae04b6d0SSong Liu# Link objects into an executable, the way the kernel's final link produces
387ae04b6d0SSong Liu# vmlinux from vmlinux.o.  Entry point 0 and no libc: nothing runs it, it only
388ae04b6d0SSong Liu# has to be a linked image with resolved addresses.
389ae04b6d0SSong Liu#
390ae04b6d0SSong Liu# The sub-sections have to come out in name order rather than object order,
391ae04b6d0SSong Liu# the way the kernel's linker script gathers .text.unlikely and .data.. apart
392ae04b6d0SSong Liu# from the rest.  That reordering is the entire reason .klp.symid exists: a
393ae04b6d0SSong Liu# link which preserves order cannot tell a correct sympos from one that merely
394ae04b6d0SSong Liu# counted, and the caller checks the two orders really did diverge.
395ae04b6d0SSong Liu#
396ae04b6d0SSong Liu# A linker script rather than --sort-section=name, because lld accepts that
397ae04b6d0SSong Liu# option and ignores it -- so on a host where only lld can link the target, the
398ae04b6d0SSong Liu# test would quietly stop testing the thing it is named for.
399ae04b6d0SSong Liu#
400ae04b6d0SSong Liu# Three attempts because a cross run has neither $LD nor the compiler's default
401ae04b6d0SSong Liu# linker able to touch the target: on an arm64 host linking x86 objects, only
402ae04b6d0SSong Liu# lld will do it.
403ae04b6d0SSong Liulink_vmlinux()
404ae04b6d0SSong Liu{
405ae04b6d0SSong Liu	local out="$1" lds="$workdir/sort.lds"; shift
406ae04b6d0SSong Liu
407ae04b6d0SSong Liu	echo 'SECTIONS { .data : { *(SORT_BY_NAME(.data.*)) } }' > "$lds"
408ae04b6d0SSong Liu
409ae04b6d0SSong Liu	$LD -e 0 -T "$lds" -o "$out" "$@" 2>/dev/null ||
410ae04b6d0SSong Liu		$CC -nostdlib -Wl,-e,0 -Wl,-T,"$lds" \
411ae04b6d0SSong Liu			-o "$out" "$@" 2>/dev/null ||
412ae04b6d0SSong Liu		$CC -nostdlib -fuse-ld=lld -Wl,-e,0 -Wl,-T,"$lds" \
413ae04b6d0SSong Liu			-o "$out" "$@" 2>/dev/null
414ae04b6d0SSong Liu}
415ae04b6d0SSong Liu
416ae04b6d0SSong Liu# make_vmlinux_pair <orig object...> -- <patched object...>
417ae04b6d0SSong Liu#
418ae04b6d0SSong Liu# Build the vmlinux.o / vmlinux pair klp diff needs to resolve sympos the way
419ae04b6d0SSong Liu# it does for built-in code, and point the diff at it.
420ae04b6d0SSong Liu#
421ae04b6d0SSong Liu# For a module, sympos is a count in symbol table order, which klp diff can do
422ae04b6d0SSong Liu# from the object alone.  vmlinux is different: the final link reorders
423ae04b6d0SSong Liu# sub-sections, so the position comes from the linked image, bridged by
424ae04b6d0SSong Liu# .klp.symid.  klp diff only looks for that when the object it was handed is
425ae04b6d0SSong Liu# called vmlinux.o and a vmlinux sits beside it -- so both the name and the
426ae04b6d0SSong Liu# linked image matter.
427ae04b6d0SSong Liumake_vmlinux_pair()
428ae04b6d0SSong Liu{
429ae04b6d0SSong Liu	local orig=() patched=() seen= arg
430ae04b6d0SSong Liu
431ae04b6d0SSong Liu	for arg in "$@"; do
432ae04b6d0SSong Liu		if [ "$arg" = -- ]; then seen=y; continue; fi
433ae04b6d0SSong Liu		if [ -n "$seen" ]; then patched+=( "$arg" ); else orig+=( "$arg" ); fi
434ae04b6d0SSong Liu	done
435ae04b6d0SSong Liu
436ae04b6d0SSong Liu	# Both sides have to have been named.  Without this, forgetting the --
437ae04b6d0SSong Liu	# leaves one list empty, the link of nothing fails, and the test skips
438ae04b6d0SSong Liu	# saying the toolchain cannot link -- which is a test bug wearing the
439ae04b6d0SSong Liu	# costume of an environment one.
440ae04b6d0SSong Liu	[ "${#orig[@]}" -gt 0 ] && [ "${#patched[@]}" -gt 0 ] ||
441ae04b6d0SSong Liu		fail "make_vmlinux_pair needs objects either side of --"
442ae04b6d0SSong Liu
443ae04b6d0SSong Liu	partial_link "$workdir/vmlinux.o" "${orig[@]}" ||
444ae04b6d0SSong Liu		probe_skip "partial link unavailable"
445ae04b6d0SSong Liu	partial_link "$workdir/patched.o" "${patched[@]}" ||
446ae04b6d0SSong Liu		probe_skip "partial link unavailable"
447ae04b6d0SSong Liu
448ae04b6d0SSong Liu	"$OBJTOOL" --klp-symids --link "$workdir/vmlinux.o" ||
449ae04b6d0SSong Liu		fail "objtool --klp-symids failed"
450ae04b6d0SSong Liu
451ae04b6d0SSong Liu	link_vmlinux "$workdir/vmlinux" "$workdir/vmlinux.o" ||
452ae04b6d0SSong Liu		probe_skip "cannot link a vmlinux here"
453ae04b6d0SSong Liu
454ae04b6d0SSong Liu	orig_obj=vmlinux.o
455ae04b6d0SSong Liu}
456ae04b6d0SSong Liu
457ae04b6d0SSong Liu# build_module_pair <fixture.c> <module name> [cflags...]
458ae04b6d0SSong Liu#
459ae04b6d0SSong Liu# Build the pair as objects belonging to a module rather than to vmlinux.  klp
460ae04b6d0SSong Liu# diff reads the object's module name from .modinfo, and that decides which
461ae04b6d0SSong Liu# object a relocation is attributed to and whether a reference counts as
462ae04b6d0SSong Liu# cross-module, so a good deal of the code has a module path the vmlinux
463ae04b6d0SSong Liu# fixtures never reach.
464ae04b6d0SSong Liu#
465ae04b6d0SSong Liu# The fixture defines its .modinfo name from MODNAME.  Passing that through
466ae04b6d0SSong Liu# -D needs two levels of quoting, which is easy to get wrong at the call site.
467ae04b6d0SSong Liubuild_module_pair()
468ae04b6d0SSong Liu{
469ae04b6d0SSong Liu	local fixture="$1" modname="$2"; shift 2
470ae04b6d0SSong Liu
471ae04b6d0SSong Liu	build_pair "$fixture" -DMODNAME="\"$modname\"" "$@"
472ae04b6d0SSong Liu}
473ae04b6d0SSong Liu
474cff27d90SPuranjay Mohan# find_thinlto_toolchain
475cff27d90SPuranjay Mohan#
476317cec90SSong Liu# Set $THIN_LD to an lld from the same LLVM release as $CC (or THIN_CC).  A
477cff27d90SPuranjay Mohan# mismatched pair fails with "Invalid summary version", which reads like a
478cff27d90SPuranjay Mohan# broken test rather than a broken environment.
479317cec90SSong Liu#
480317cec90SSong Liu# ThinLTO is clang-only; callers must use clang_only before calling this.
481317cec90SSong Liu# Only $CC (or an explicit THIN_CC override) is consulted -- the harness does
482317cec90SSong Liu# not search for a second compiler beside a gcc $CC.
483cff27d90SPuranjay Mohanfind_thinlto_toolchain()
484cff27d90SPuranjay Mohan{
485317cec90SSong Liu	local cc ver ld
486cff27d90SPuranjay Mohan
487317cec90SSong Liu	for cc in "${THIN_CC:-}" "$CC"; do
488cff27d90SPuranjay Mohan		[ -n "$cc" ] || continue
489317cec90SSong Liu		command -v "${cc%% *}" >/dev/null 2>&1 || return 1
490cff27d90SPuranjay Mohan
491cff27d90SPuranjay Mohan		ver=$($cc -dumpversion 2>/dev/null | cut -d. -f1)
492cff27d90SPuranjay Mohan
493cff27d90SPuranjay Mohan		for ld in "${THIN_LD:-}" "ld.lld-$ver" ld.lld; do
494cff27d90SPuranjay Mohan			[ -n "$ld" ] || continue
495cff27d90SPuranjay Mohan			command -v "$ld" >/dev/null 2>&1 || continue
496cff27d90SPuranjay Mohan
497cff27d90SPuranjay Mohan			echo 'int probe(void) { return 0; }' > "$workdir/probe.c"
498cff27d90SPuranjay Mohan			$cc -flto=thin -O2 -c "$workdir/probe.c" \
499cff27d90SPuranjay Mohan				-o "$workdir/probe.o" 2>/dev/null || continue
500cff27d90SPuranjay Mohan			"$ld" -r "$workdir/probe.o" -o "$workdir/probe.elf" \
501cff27d90SPuranjay Mohan				2>/dev/null || continue
502cff27d90SPuranjay Mohan
503cff27d90SPuranjay Mohan			THIN_CC="$cc"
504cff27d90SPuranjay Mohan			THIN_LD="$ld"
505cff27d90SPuranjay Mohan			return 0
506cff27d90SPuranjay Mohan		done
507cff27d90SPuranjay Mohan	done
508cff27d90SPuranjay Mohan
509cff27d90SPuranjay Mohan	return 1
510cff27d90SPuranjay Mohan}
511cff27d90SPuranjay Mohan
512c009fabcSSong Liuout_sections() { $READELF -S -W "$workdir/out.o" 2>/dev/null; }
513c009fabcSSong Liuout_relocs()   { $READELF -r -W "$workdir/out.o" 2>/dev/null; }
514c009fabcSSong Liuout_symbols()  { $READELF -s -W "$workdir/out.o" 2>/dev/null; }
515cff27d90SPuranjay Mohandiff_log()     { cat "$workdir/diff.log"; }
516cff27d90SPuranjay Mohan
517*2ab0faebSSong Liu# out_strings <section>
518*2ab0faebSSong Liu#
519*2ab0faebSSong Liu# The strings in one section of the output, for the names livepatch matches on.
520*2ab0faebSSong Liuout_strings() { $READELF -p "$1" "$workdir/out.o" 2>/dev/null; }
521*2ab0faebSSong Liu
522*2ab0faebSSong Liu# Checks on the input objects, to run before klp diff.  The two forms differ in
523*2ab0faebSSong Liu# what an absent construct means:
524*2ab0faebSSong Liu#
525*2ab0faebSSong Liu#   require_*  the compiler cannot produce it here	-> skip
526*2ab0faebSSong Liu#   assert_*   the fixture is supposed to produce it	-> fail
527*2ab0faebSSong Liu
528*2ab0faebSSong Liuin_sections() { $READELF -S -W "$workdir/$1" 2>/dev/null; }
529*2ab0faebSSong Liuin_symbols()  { $READELF -s -W "$workdir/$1" 2>/dev/null; }
530*2ab0faebSSong Liuin_relocs()   { $READELF -r -W "$workdir/$1" 2>/dev/null; }
531*2ab0faebSSong Liu
532*2ab0faebSSong Liu# count_input_symbols <object> <name>
533*2ab0faebSSong Liu#
534*2ab0faebSSong Liu# How many object symbols of exactly that name the input has.  Deliberately not
535*2ab0faebSSong Liu# a grep: readelf lists section symbols too, and a newer binutils prints their
536*2ab0faebSSong Liu# name -- ".data.<name>" -- where an older one leaves the column blank.  A dot
537*2ab0faebSSong Liu# is not a word character, so "grep -w <name>" counts that line as well, and
538*2ab0faebSSong Liu# the same object gives a different answer depending on which readelf reads it.
539*2ab0faebSSong Liucount_input_symbols()
540*2ab0faebSSong Liu{
541*2ab0faebSSong Liu	in_symbols "$1" | awk -v n="$2" '$4 == "OBJECT" && $8 == n' | wc -l
542*2ab0faebSSong Liu}
543*2ab0faebSSong Liu
544*2ab0faebSSong Liu# re_quote <string>
545*2ab0faebSSong Liu#
546*2ab0faebSSong Liu# A string as a literal basic regular expression.  Nearly every name these
547*2ab0faebSSong Liu# assertions match on contains a dot -- .text.target, .klp.rela.vmlinux -- and
548*2ab0faebSSong Liu# an unescaped dot matches any character, so an assertion for one section can be
549*2ab0faebSSong Liu# satisfied by a different one whose name merely lines up.
550*2ab0faebSSong Liure_quote() { printf '%s' "$1" | sed 's|[].[^$*\\/]|\\&|g'; }
551*2ab0faebSSong Liu
552*2ab0faebSSong Liuhas_input_section() { in_sections "$1" | grep -q "[[:space:]]$(re_quote "$2")[[:space:]]"; }
553*2ab0faebSSong Liuhas_input_symbol()  { in_symbols "$1" | awk -v n="$2" '$NF == n' | grep -q .; }
554*2ab0faebSSong Liu
555*2ab0faebSSong Liuassert_input_section()
556*2ab0faebSSong Liu{
557*2ab0faebSSong Liu	local obj
558*2ab0faebSSong Liu
559*2ab0faebSSong Liu	for obj in "$orig_obj" "$patched_obj"; do
560*2ab0faebSSong Liu		has_input_section "$obj" "$1" ||
561*2ab0faebSSong Liu			fail "fixture produced no section '$1' in $obj"
562*2ab0faebSSong Liu	done
563*2ab0faebSSong Liu}
564*2ab0faebSSong Liu
565*2ab0faebSSong Liuassert_input_symbol()
566*2ab0faebSSong Liu{
567*2ab0faebSSong Liu	local obj
568*2ab0faebSSong Liu
569*2ab0faebSSong Liu	for obj in "$orig_obj" "$patched_obj"; do
570*2ab0faebSSong Liu		has_input_symbol "$obj" "$1" ||
571*2ab0faebSSong Liu			fail "fixture produced no symbol '$1' in $obj"
572*2ab0faebSSong Liu	done
573*2ab0faebSSong Liu}
574*2ab0faebSSong Liu
575*2ab0faebSSong Liurequire_input_section()
576*2ab0faebSSong Liu{
577*2ab0faebSSong Liu	local obj
578*2ab0faebSSong Liu
579*2ab0faebSSong Liu	for obj in "$orig_obj" "$patched_obj"; do
580*2ab0faebSSong Liu		has_input_section "$obj" "$1" ||
581*2ab0faebSSong Liu			probe_skip "compiler produced no section '$1' here"
582*2ab0faebSSong Liu	done
583*2ab0faebSSong Liu}
584*2ab0faebSSong Liu
585cff27d90SPuranjay Mohanassert_section()
586cff27d90SPuranjay Mohan{
587*2ab0faebSSong Liu	out_sections | grep -q "[[:space:]]$(re_quote "$1")[[:space:]]" ||
588cff27d90SPuranjay Mohan		fail "expected section '$1' in output"
589cff27d90SPuranjay Mohan}
590cff27d90SPuranjay Mohan
591*2ab0faebSSong Liuassert_no_section()
592*2ab0faebSSong Liu{
593*2ab0faebSSong Liu	out_sections | grep -q "[[:space:]]$(re_quote "$1")[[:space:]]" &&
594*2ab0faebSSong Liu		fail "unexpected section '$1' in output"
595*2ab0faebSSong Liu	return 0
596*2ab0faebSSong Liu}
597*2ab0faebSSong Liu
598cff27d90SPuranjay Mohanassert_patched()
599cff27d90SPuranjay Mohan{
600cff27d90SPuranjay Mohan	assert_section ".text.$1"
601cff27d90SPuranjay Mohan}
602cff27d90SPuranjay Mohan
603cff27d90SPuranjay Mohanassert_not_patched()
604cff27d90SPuranjay Mohan{
605*2ab0faebSSong Liu	out_sections | grep -q "[[:space:]]$(re_quote ".text.$1")[[:space:]]" &&
606cff27d90SPuranjay Mohan		fail "function '$1' should not have been cloned"
607cff27d90SPuranjay Mohan	return 0
608cff27d90SPuranjay Mohan}
609*2ab0faebSSong Liu
610*2ab0faebSSong Liu# section_relocs <section>
611*2ab0faebSSong Liu#
612*2ab0faebSSong Liu# The relocations against one section.  readelf prints every relocation section
613*2ab0faebSSong Liu# in turn, so a test asking about ".smp_locks" has to cut its block out of the
614*2ab0faebSSong Liu# listing first.
615*2ab0faebSSong Liusection_relocs()
616*2ab0faebSSong Liu{
617*2ab0faebSSong Liu	local sec="${1//./\\.}"
618*2ab0faebSSong Liu
619*2ab0faebSSong Liu	out_relocs | awk "/rela$sec'/,/^\$/"
620*2ab0faebSSong Liu}
621*2ab0faebSSong Liu
622*2ab0faebSSong Liuassert_reloc_sym()
623*2ab0faebSSong Liu{
624*2ab0faebSSong Liu	section_relocs "$1" | awk -v n="$2" '$5 == n' | grep -q . ||
625*2ab0faebSSong Liu		fail "expected a relocation to '$2' in '$1'"
626*2ab0faebSSong Liu}
627*2ab0faebSSong Liu
628*2ab0faebSSong Liuassert_no_reloc_sym()
629*2ab0faebSSong Liu{
630*2ab0faebSSong Liu	section_relocs "$1" | awk -v n="$2" '$5 == n' | grep -q . &&
631*2ab0faebSSong Liu		fail "unexpected relocation to '$2' in '$1'"
632*2ab0faebSSong Liu	return 0
633*2ab0faebSSong Liu}
634*2ab0faebSSong Liu
635*2ab0faebSSong Liu# assert_reloc_count <section> <count>
636*2ab0faebSSong Liu#
637*2ab0faebSSong Liu# Counts relocation entries, not header or blank lines: whether a special
638*2ab0faebSSong Liu# section entry was extracted once, twice or not at all is usually the whole
639*2ab0faebSSong Liu# question.
640*2ab0faebSSong Liu#
641*2ab0faebSSong Liu# A count of zero is ambiguous on its own -- a section with no relocations and
642*2ab0faebSSong Liu# no section at all both read as zero -- so require the section to exist.  A
643*2ab0faebSSong Liu# test expecting nothing there wants assert_no_section.
644*2ab0faebSSong Liuassert_reloc_count()
645*2ab0faebSSong Liu{
646*2ab0faebSSong Liu	local n
647*2ab0faebSSong Liu
648*2ab0faebSSong Liu	assert_section "$1"
649*2ab0faebSSong Liu
650*2ab0faebSSong Liu	n="$(section_relocs "$1" | grep -cE '^[0-9a-f]{8,}')"
651*2ab0faebSSong Liu	[ "$n" = "$2" ] ||
652*2ab0faebSSong Liu		fail "expected $2 relocations in '$1', found $n"
653*2ab0faebSSong Liu}
654*2ab0faebSSong Liu
655*2ab0faebSSong Liu# assert_klp_sym <symbol> [object]
656*2ab0faebSSong Liu#
657*2ab0faebSSong Liu# A klp symbol is named .klp.sym.<object>.<symbol>,<sympos>.  The object
658*2ab0faebSSong Liu# defaults to any, since most tests care that the reference was converted at
659*2ab0faebSSong Liu# all rather than which object it resolved against.
660*2ab0faebSSong Liuassert_klp_sym()
661*2ab0faebSSong Liu{
662*2ab0faebSSong Liu	out_symbols | grep -q "\.klp\.sym\.${2:-[^.]*}\.$(re_quote "$1")," ||
663*2ab0faebSSong Liu		fail "expected klp symbol for '$1'"
664*2ab0faebSSong Liu}
665*2ab0faebSSong Liu
666*2ab0faebSSong Liu# assert_klp_sympos <symbol> <sympos>
667*2ab0faebSSong Liu#
668*2ab0faebSSong Liu# The number after the comma in .klp.sym.<object>.<symbol>,<sympos> says which
669*2ab0faebSSong Liu# of several same-named symbols livepatch should resolve to, counting from 1;
670*2ab0faebSSong Liu# 0 means the name is unique and no disambiguation is needed.  Resolving to the
671*2ab0faebSSong Liu# wrong one is not a load failure, it is a patch quietly wired to the wrong
672*2ab0faebSSong Liu# object.
673*2ab0faebSSong Liuassert_klp_sympos()
674*2ab0faebSSong Liu{
675*2ab0faebSSong Liu	out_symbols | grep -qE "\.klp\.sym\.[^.]+\.$(re_quote "$1"),$2([[:space:]]|\$)" ||
676*2ab0faebSSong Liu		fail "expected klp symbol for '$1' with sympos $2, found:$(
677*2ab0faebSSong Liu			out_symbols | grep -o "\.klp\.sym\.[^.]*\.$(re_quote "$1"),[0-9]*" |
678*2ab0faebSSong Liu			sort -u | tr '\n' ' ')"
679*2ab0faebSSong Liu}
680*2ab0faebSSong Liu
681*2ab0faebSSong Liuassert_no_klp_sym()
682*2ab0faebSSong Liu{
683*2ab0faebSSong Liu	out_symbols | grep -q "\.klp\.sym\.${2:-[^.]*}\.$(re_quote "$1")," &&
684*2ab0faebSSong Liu		fail "unexpected klp symbol for '$1'"
685*2ab0faebSSong Liu	return 0
686*2ab0faebSSong Liu}
687*2ab0faebSSong Liu
688*2ab0faebSSong Liuassert_tombstone()
689*2ab0faebSSong Liu{
690*2ab0faebSSong Liu	out_symbols | grep -qE "\.klp\.tombstone\.$(re_quote "$1")([[:space:]]|\$)" ||
691*2ab0faebSSong Liu		fail "expected a tombstone for '$1'"
692*2ab0faebSSong Liu}
693*2ab0faebSSong Liu
694*2ab0faebSSong Liuassert_symbol()
695*2ab0faebSSong Liu{
696*2ab0faebSSong Liu	out_symbols | awk -v n="$1" '$NF == n' | grep -q . ||
697*2ab0faebSSong Liu		fail "expected symbol '$1' in output"
698*2ab0faebSSong Liu}
699*2ab0faebSSong Liu
700*2ab0faebSSong Liuassert_no_symbol()
701*2ab0faebSSong Liu{
702*2ab0faebSSong Liu	out_symbols | awk -v n="$1" '$NF == n' | grep -q . &&
703*2ab0faebSSong Liu		fail "unexpected symbol '$1' in output"
704*2ab0faebSSong Liu	return 0
705*2ab0faebSSong Liu}
706*2ab0faebSSong Liu
707*2ab0faebSSong Liu# assert_diff_log <regex>
708*2ab0faebSSong Liu#
709*2ab0faebSSong Liu# klp diff's combined output, for tests asserting on a diagnostic.  Error
710*2ab0faebSSong Liu# messages are part of the interface when the whole point is that a construct
711*2ab0faebSSong Liu# gets rejected, and a rejection for the wrong reason is not a pass.
712*2ab0faebSSong Liuassert_diff_log()
713*2ab0faebSSong Liu{
714*2ab0faebSSong Liu	diff_log | grep -qE -- "$1" ||
715*2ab0faebSSong Liu		fail "expected '$1' in klp diff output: $(tail -2 "$workdir/diff.log")"
716*2ab0faebSSong Liu}
717*2ab0faebSSong Liu
718*2ab0faebSSong Liu# checksum_of <object> <symbol>
719*2ab0faebSSong Liu#
720*2ab0faebSSong Liu# The checksum "klp checksum" recorded for one symbol, as a hex string.
721*2ab0faebSSong Liu#
722*2ab0faebSSong Liu# .discard.sym_checksum is an array of { u64 addr; u64 checksum; }, where addr
723*2ab0faebSSong Liu# is the target of a relocation naming the symbol.  Nothing in the section
724*2ab0faebSSong Liu# itself says which symbol an entry belongs to, so the relocation is what
725*2ab0faebSSong Liu# locates the entry; the checksum is the eight bytes after it.
726*2ab0faebSSong Liu# Callers use this in a command substitution, where fail() would only exit the
727*2ab0faebSSong Liu# subshell and the test would carry on with an empty checksum.  So this returns
728*2ab0faebSSong Liu# non-zero and prints nothing, and the assertions below check for that.  For
729*2ab0faebSSong Liu# the same reason it does not run run_checksum() itself: that one does call
730*2ab0faebSSong Liu# fail(), and from in here the message would be captured as the checksum
731*2ab0faebSSong Liu# rather than ending the test.  The caller runs it first.
732*2ab0faebSSong Liuchecksum_of()
733*2ab0faebSSong Liu{
734*2ab0faebSSong Liu	local obj="$workdir/$1" sym="$2" off
735*2ab0faebSSong Liu
736*2ab0faebSSong Liu	off="$($READELF -rW "$obj" 2>/dev/null |
737*2ab0faebSSong Liu	       awk -v s="$sym" '/rela\.discard\.sym_checksum/,/^$/ {
738*2ab0faebSSong Liu			if ($5 == s) { print $1; exit }
739*2ab0faebSSong Liu	       }')"
740*2ab0faebSSong Liu
741*2ab0faebSSong Liu	[ -n "$off" ] || return 1
742*2ab0faebSSong Liu
743*2ab0faebSSong Liu	$OBJCOPY -O binary --only-section=.discard.sym_checksum \
744*2ab0faebSSong Liu		"$obj" "$workdir/checksums.bin" 2>/dev/null || return 1
745*2ab0faebSSong Liu
746*2ab0faebSSong Liu	dd if="$workdir/checksums.bin" bs=1 skip=$((16#$off + 8)) count=8 \
747*2ab0faebSSong Liu		status=none | od -An -tx1 | tr -d ' \n'
748*2ab0faebSSong Liu}
749*2ab0faebSSong Liu
750*2ab0faebSSong Liu# assert_checksum_differs <symbol> / assert_checksum_matches <symbol>
751*2ab0faebSSong Liu#
752*2ab0faebSSong Liu# Compare what klp checksum recorded for a symbol in the original against the
753*2ab0faebSSong Liu# patched object.  This is what decides whether klp diff treats a function as
754*2ab0faebSSong Liu# changed, so a test asserting only that the right functions were cloned cannot
755*2ab0faebSSong Liu# tell a correct checksum from one which happens to differ.
756*2ab0faebSSong Liuchecksum_pair()
757*2ab0faebSSong Liu{
758*2ab0faebSSong Liu	run_checksum
759*2ab0faebSSong Liu
760*2ab0faebSSong Liu	orig_checksum="$(checksum_of "$orig_obj" "$1")"
761*2ab0faebSSong Liu	patched_checksum="$(checksum_of "$patched_obj" "$1")"
762*2ab0faebSSong Liu
763*2ab0faebSSong Liu	[ -n "$orig_checksum" ] ||
764*2ab0faebSSong Liu		fail "no checksum recorded for '$1' in $orig_obj"
765*2ab0faebSSong Liu	[ -n "$patched_checksum" ] ||
766*2ab0faebSSong Liu		fail "no checksum recorded for '$1' in $patched_obj"
767*2ab0faebSSong Liu}
768*2ab0faebSSong Liu
769*2ab0faebSSong Liuassert_checksum_differs()
770*2ab0faebSSong Liu{
771*2ab0faebSSong Liu	checksum_pair "$1"
772*2ab0faebSSong Liu
773*2ab0faebSSong Liu	[ "$orig_checksum" != "$patched_checksum" ] ||
774*2ab0faebSSong Liu		fail "checksum for '$1' unchanged at $orig_checksum, expected it to differ"
775*2ab0faebSSong Liu}
776*2ab0faebSSong Liu
777*2ab0faebSSong Liuassert_checksum_matches()
778*2ab0faebSSong Liu{
779*2ab0faebSSong Liu	checksum_pair "$1"
780*2ab0faebSSong Liu
781*2ab0faebSSong Liu	[ "$orig_checksum" = "$patched_checksum" ] ||
782*2ab0faebSSong Liu		fail "checksum for '$1' changed from $orig_checksum to" \
783*2ab0faebSSong Liu		     "$patched_checksum, expected no change"
784*2ab0faebSSong Liu}
785*2ab0faebSSong Liu
786*2ab0faebSSong Liu# run_post_link [expected exit status]
787*2ab0faebSSong Liu#
788*2ab0faebSSong Liu# klp post-link runs last in a livepatch build, converting the intermediate
789*2ab0faebSSong Liu# __klp_relocs.* sections into the .klp.rela.* form the kernel consumes.  It
790*2ab0faebSSong Liu# needs nothing but an object containing those sections, which is what klp diff
791*2ab0faebSSong Liu# produces, so it runs on out.o here rather than on a built module.  Rewrites
792*2ab0faebSSong Liu# out.o in place, so the out_* helpers show the result afterwards.
793*2ab0faebSSong Liurun_post_link()
794*2ab0faebSSong Liu{
795*2ab0faebSSong Liu	local expect="${1:-0}" rc=0
796*2ab0faebSSong Liu
797*2ab0faebSSong Liu	"$OBJTOOL" klp post-link "$workdir/out.o" \
798*2ab0faebSSong Liu		> "$workdir/post-link.log" 2>&1 || rc=$?
799*2ab0faebSSong Liu
800*2ab0faebSSong Liu	[ "$rc" = "$expect" ] ||
801*2ab0faebSSong Liu		fail "klp post-link exited $rc, expected $expect:" \
802*2ab0faebSSong Liu		     "$(tail -2 "$workdir/post-link.log")"
803*2ab0faebSSong Liu}
804*2ab0faebSSong Liu
805*2ab0faebSSong Liu# The flags readelf prints for a section, or nothing when it has none.  The
806*2ab0faebSSong Liu# leading "[nn]" index is stripped first so the columns can be counted.
807*2ab0faebSSong Liusection_flags()
808*2ab0faebSSong Liu{
809*2ab0faebSSong Liu	out_sections | sed 's/^ *\[[ 0-9]*\] *//' |
810*2ab0faebSSong Liu		awk -v s="$1" '$1 == s && $7 ~ /^[A-Za-z]+$/ { print $7 }'
811*2ab0faebSSong Liu}
812*2ab0faebSSong Liu
813*2ab0faebSSong Liu# assert_section_flag <section> <letter>
814*2ab0faebSSong Liu#
815*2ab0faebSSong Liu# SHF_RELA_LIVEPATCH is OS-specific, so readelf renders it as "o".  A klp rela
816*2ab0faebSSong Liu# section which lost it is an ordinary rela section, which the linker may apply
817*2ab0faebSSong Liu# and the livepatch code will not.
818*2ab0faebSSong Liuassert_section_flag()
819*2ab0faebSSong Liu{
820*2ab0faebSSong Liu	local flags; flags="$(section_flags "$1")"
821*2ab0faebSSong Liu
822*2ab0faebSSong Liu	[ -n "$flags" ] ||
823*2ab0faebSSong Liu		fail "section '$1' has no flags, expected '$2'"
824*2ab0faebSSong Liu	case "$flags" in
825*2ab0faebSSong Liu	*"$2"*)	;;
826*2ab0faebSSong Liu	*)	fail "section '$1' has flags '$flags', expected '$2'" ;;
827*2ab0faebSSong Liu	esac
828*2ab0faebSSong Liu}
829*2ab0faebSSong Liu
830*2ab0faebSSong Liu# assert_klp_rela <object> <section>
831*2ab0faebSSong Liu#
832*2ab0faebSSong Liu# post-link names the converted sections .klp.rela.<object>.<section>, one per
833*2ab0faebSSong Liu# base section.  Also checks SHF_RELA_LIVEPATCH, since the name alone is not
834*2ab0faebSSong Liu# what makes the kernel process it.
835*2ab0faebSSong Liuassert_klp_rela()
836*2ab0faebSSong Liu{
837*2ab0faebSSong Liu	local name=".klp.rela.$1.$2"
838*2ab0faebSSong Liu
839*2ab0faebSSong Liu	out_sections | grep -q "[[:space:]]$(re_quote "$name")[[:space:]]" ||
840*2ab0faebSSong Liu		fail "expected section '$name' in output"
841*2ab0faebSSong Liu
842*2ab0faebSSong Liu	assert_section_flag "$name" o
843*2ab0faebSSong Liu}
844*2ab0faebSSong Liu
845*2ab0faebSSong Liu# assert_livepatch_sym <symbol>
846*2ab0faebSSong Liu#
847*2ab0faebSSong Liu# Symbols a klp relocation resolves against live in SHN_LIVEPATCH, which
848*2ab0faebSSong Liu# readelf prints as "OS [0xff20]" -- llvm-readelf without the space, so match
849*2ab0faebSSong Liu# either.  The kernel resolves these itself at patch load; anything else is a
850*2ab0faebSSong Liu# symbol the module loader will try, and fail, to resolve normally.
851*2ab0faebSSong Liuassert_livepatch_sym()
852*2ab0faebSSong Liu{
853*2ab0faebSSong Liu	out_symbols | grep -E 'OS ?\[0xff20\]' |
854*2ab0faebSSong Liu		grep -qE "\.klp\.sym\.[^.]+\.$(re_quote "$1")," ||
855*2ab0faebSSong Liu		fail "expected a klp symbol for '$1' in SHN_LIVEPATCH"
856*2ab0faebSSong Liu}
857