xref: /linux/tools/objtool/tests/lib.sh (revision 9b11acaad1a63eddf4a9bf50dc9b90183bf037e1)
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
1148dafeeaSSong Liuset -u
1248dafeeaSSong Liu
13cff27d90SPuranjay MohanTESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14d76312b8SSong Liu
15d76312b8SSong Liu# Tests live in generic/ or in an architecture directory beside it, and each
16d76312b8SSong Liu# carries its own fixtures.
17d76312b8SSong LiuFIXTURES_DIR="$(cd "$(dirname "$0")/fixtures" 2>/dev/null && pwd)"
18cff27d90SPuranjay Mohan
19c009fabcSSong Liu# The kernel's convention: CROSS_COMPILE is the one knob, with per-tool
20c009fabcSSong Liu# overrides for what it does not cover.  objtool itself is always a host binary
21c009fabcSSong Liu# -- it is built with HOSTCC and only reads ELF -- so an arm64 machine can run
22c009fabcSSong Liu# the x86 tests against x86 objects given a compiler that emits them.
23c009fabcSSong Liu#
24c009fabcSSong Liu# readelf reads any target, so it rarely needs overriding, and either GNU
25c009fabcSSong Liu# readelf or llvm-readelf will do: the assertions match on fields rather than
26c009fabcSSong Liu# on columns, and where the two spell something differently -- "OS [0xff20]"
27c009fabcSSong Liu# against "OS[0xff20]" for SHN_LIVEPATCH -- they accept both.  BFD's objcopy is
28c009fabcSSong Liu# usually built for the host's target alone, and llvm-objcopy is the
29c009fabcSSong Liu# target-agnostic replacement.
30c009fabcSSong LiuCROSS_COMPILE="${CROSS_COMPILE:-}"
31c009fabcSSong LiuCC="${CC:-${CROSS_COMPILE}gcc}"
32c009fabcSSong LiuLD="${LD:-${CROSS_COMPILE}ld}"
33c009fabcSSong LiuREADELF="${READELF:-${CROSS_COMPILE}readelf}"
34c009fabcSSong LiuOBJCOPY="${OBJCOPY:-${CROSS_COMPILE}objcopy}"
35c009fabcSSong Liu
36cff27d90SPuranjay MohanOBJTOOL="${OBJTOOL:-$TESTS_DIR/../objtool}"
37c009fabcSSong Liu
38c009fabcSSong Liu# klp_preflight
39c009fabcSSong Liu#
40c009fabcSSong Liu# Check the environment once, before any test runs, and report what was found.
41c009fabcSSong Liu#
42c009fabcSSong Liuklp_preflight()
43c009fabcSSong Liu{
44d76312b8SSong Liu	local tmp tool cc_version arch host cc_arch
45c009fabcSSong Liu
46c009fabcSSong Liu	bail() { echo "Bail out! $*" >&2; exit 1; }
47c009fabcSSong Liu
48c009fabcSSong Liu	# A relative $OBJTOOL is relative to the objtool directory, not tests/.
49c009fabcSSong Liu	[ -x "$OBJTOOL" ] || [ ! -x "$TESTS_DIR/../$OBJTOOL" ] ||
50c009fabcSSong Liu		OBJTOOL="$TESTS_DIR/../$OBJTOOL"
51c009fabcSSong Liu
52c009fabcSSong Liu	[ -x "$OBJTOOL" ] ||
53c009fabcSSong Liu		bail "objtool not found at '$OBJTOOL' -- build it first"
54c009fabcSSong Liu
55c009fabcSSong Liu	# run_diff() runs objtool from inside the test's working directory, so
56c009fabcSSong Liu	# a relative path would resolve against that instead.
57c009fabcSSong Liu	OBJTOOL="$(realpath "$OBJTOOL")"
58c009fabcSSong Liu
59c009fabcSSong Liu	"$OBJTOOL" klp 2>&1 | grep -q checksum ||
60c009fabcSSong Liu		bail "objtool was built without klp support; install libxxhash (>= 0.8) and rebuild"
61c009fabcSSong Liu
62c009fabcSSong Liu	command -v "${CC%% *}" >/dev/null || bail "compiler not found: $CC"
63c009fabcSSong Liu
64c009fabcSSong Liu	for tool in "$READELF" "$OBJCOPY" "$LD"; do
65c009fabcSSong Liu		command -v "${tool%% *}" >/dev/null || bail "$tool not found"
66c009fabcSSong Liu	done
67c009fabcSSong Liu
68c009fabcSSong Liu	tmp="$(mktemp -d)" || bail "mktemp failed"
69c009fabcSSong Liu	echo 'int probe(void) { return 0; }' > "$tmp/probe.c"
70c009fabcSSong Liu	$CC -c -o "$tmp/probe.o" "$tmp/probe.c" 2>/dev/null ||
71c009fabcSSong Liu		{ rm -rf "$tmp"; bail "$CC cannot compile a trivial object"; }
72c009fabcSSong Liu
73c009fabcSSong Liu	# $CC, $ARCH and objtool have to agree about the target, and cross runs
74c009fabcSSong Liu	# are where they stop agreeing: plain "CC=clang ARCH=x86_64" on an arm64
75c009fabcSSong Liu	# box selects the x86 tests and then builds arm64 objects, because clang
76c009fabcSSong Liu	# needs --target= to emit anything but the host's.
77c009fabcSSong Liu	#
78c009fabcSSong Liu	# Ask objtool rather than comparing machine names.  It rejects an object
79c009fabcSSong Liu	# it was not built for -- "unexpected ELF machine type" -- so one check
80c009fabcSSong Liu	# covers every way the three can disagree, and says so once instead of
81c009fabcSSong Liu	# failing every test for the same reason.
82c009fabcSSong Liu	"$OBJTOOL" klp checksum "$tmp/probe.o" >/dev/null 2>&1 ||
83c009fabcSSong Liu		{ rm -rf "$tmp"
84c009fabcSSong Liu		  bail "objtool rejects an object built by '$CC'; they target" \
85c009fabcSSong Liu		       "different architectures (set CROSS_COMPILE, or" \
86c009fabcSSong Liu		       "--target= for clang)"; }
87c009fabcSSong Liu
88c009fabcSSong Liu	# BFD objcopy is usually built for the host's target alone, and
89c009fabcSSong Liu	# checksum_of() needs it to read the object under test.
90c009fabcSSong Liu	$OBJCOPY -O binary --only-section=.text "$tmp/probe.o" "$tmp/probe.bin" 2>/dev/null ||
91c009fabcSSong Liu		{ rm -rf "$tmp"
92c009fabcSSong Liu		  bail "$OBJCOPY cannot read objects built by '$CC'; install" \
93c009fabcSSong Liu		       "binutils-multiarch or set OBJCOPY=llvm-objcopy"; }
94c009fabcSSong Liu	# $ARCH only chooses which directory of tests runs, so it can disagree
95c009fabcSSong Liu	# with what $CC builds without objtool noticing -- and the result is the
96c009fabcSSong Liu	# wrong set of tests, quietly.
97c009fabcSSong Liu	case "$($READELF -hW "$tmp/probe.o" | sed -n 's/.*Machine: *//p')" in
98c009fabcSSong Liu	*X86-64*|*Intel*80386*)	cc_arch=x86 ;;
99c009fabcSSong Liu	*AArch64*)		cc_arch=arm64 ;;
100c009fabcSSong Liu	*)			cc_arch= ;;
101c009fabcSSong Liu	esac
102c009fabcSSong Liu	rm -rf "$tmp"
103c009fabcSSong Liu
104d76312b8SSong Liu	# Normalize to the kernel's SRCARCH.
105d76312b8SSong Liu	case "${ARCH:-$(uname -m)}" in
106d76312b8SSong Liu	x86_64|i?86)	arch=x86 ;;
107d76312b8SSong Liu	aarch64*)	arch=arm64 ;;
108d76312b8SSong Liu	*)		arch="${ARCH:-$(uname -m)}" ;;
109d76312b8SSong Liu	esac
110d76312b8SSong Liu
111d76312b8SSong Liu	case "$(uname -m)" in
112d76312b8SSong Liu	x86_64|i?86)	host=x86 ;;
113d76312b8SSong Liu	aarch64*)	host=arm64 ;;
114d76312b8SSong Liu	*)		host="$(uname -m)" ;;
115d76312b8SSong Liu	esac
116d76312b8SSong Liu
117d76312b8SSong Liu	[ -z "$cc_arch" ] || [ "$cc_arch" = "$arch" ] ||
118d76312b8SSong Liu		bail "ARCH says $arch but '$CC' builds $cc_arch objects;" \
119d76312b8SSong Liu		     "the $arch tests would run against the wrong architecture"
120d76312b8SSong Liu
121d76312b8SSong Liu	KLP_TEST_ARCH="$arch"
122c009fabcSSong Liu	KLP_TEST_PREFLIGHT=done
123d76312b8SSong Liu	export OBJTOOL CC KLP_TEST_ARCH KLP_TEST_PREFLIGHT
124c009fabcSSong Liu
125c009fabcSSong Liu	cc_version="$($CC --version 2>/dev/null | head -1)"
126c009fabcSSong Liu	cat <<EOF
127c009fabcSSong Liu# preflight
128c009fabcSSong Liu#   objtool   $OBJTOOL (klp: yes)
129c009fabcSSong Liu#   compiler  $cc_version
130c009fabcSSong Liu#   arch      $KLP_TEST_ARCH$([ "$arch" = "$host" ] || echo "  (host $host, cross)")
1318593055bSSong Liu#   tmpdir    ${TMPDIR:-/tmp}  (each test builds in a fresh directory here)
132c009fabcSSong LiuEOF
133c009fabcSSong Liu}
134c009fabcSSong Liu
135c009fabcSSong Liu[ -n "${KLP_TEST_PREFLIGHT:-}" ] || klp_preflight
136cff27d90SPuranjay Mohan
137ae04b6d0SSong Liu# What every fixture is built with.  These describe the kernel a fixture stands
138ae04b6d0SSong Liu# in for; -c is build_one's contract rather than a property of that kernel, so
139ae04b6d0SSong Liu# it lives at the compile where an override cannot drop it.
140ae04b6d0SSong Liu#
141ae04b6d0SSong Liu#   -O2					the kernel's default
142ae04b6d0SSong Liu#   -ffunction-sections -fdata-sections	klp-build passes these itself, through
143ae04b6d0SSong Liu#					KCFLAGS, whatever the configuration
144ae04b6d0SSong Liu#   -fno-asynchronous-unwind-tables	arch/x86/Makefile sets this always, so
145ae04b6d0SSong Liu#					kernel objects carry no .eh_frame
146ae04b6d0SSong Liu#   -fno-common				the kernel's Makefile sets it, so an
147ae04b6d0SSong Liu#					uninitialised global there lands in
148ae04b6d0SSong Liu#					.bss rather than being SHN_COMMON,
149ae04b6d0SSong Liu#					which has no section and so no
150ae04b6d0SSong Liu#					checksum
151ae04b6d0SSong Liu#
152ae04b6d0SSong Liu# A test overrides it; see tools/objtool/Documentation/klp-write-tests.txt.
153ae04b6d0SSong LiuFIXTURE_CFLAGS="-O2 -ffunction-sections -fdata-sections -fno-common \
154ae04b6d0SSong Liu		-fno-asynchronous-unwind-tables"
155cff27d90SPuranjay Mohan
156cff27d90SPuranjay Mohantest_name="$(basename "$0" .sh)"
157cff27d90SPuranjay Mohanworkdir=
158cff27d90SPuranjay Mohan
159ae04b6d0SSong Liu# The pair run_diff() and the checksum helpers work on.  build_pair() names
160ae04b6d0SSong Liu# them again and make_vmlinux_pair() repoints orig_obj at the image it links,
161ae04b6d0SSong Liu# but a test which builds its objects itself with build_one() sets neither, so
162ae04b6d0SSong Liu# the default belongs here.
163ae04b6d0SSong Liuorig_obj=orig.o
164ae04b6d0SSong Liupatched_obj=patched.o
165ae04b6d0SSong Liu
166*9b11acaaSJoe Lawrencepass() { KLP_TEST_REPORTED=1; echo "ok - $test_name${1:+: $1}"; exit 0; }
167*9b11acaaSJoe Lawrencefail() { KLP_TEST_FAILED=1; echo "not ok - $test_name: $*"; exit 1; }
168317cec90SSong Liu
169317cec90SSong Liu# Two kinds of skip, and the runner tells them apart.
170317cec90SSong Liu#
171317cec90SSong Liu#   declared_skip  the test said in advance it does not apply here, e.g.
172317cec90SSong Liu#                  gcc_only on a clang run.  Expected indefinitely.
173317cec90SSong Liu#   probe_skip     the construct did not turn up in the built object this
174317cec90SSong Liu#                  time.  Weaker: it may appear on another compiler version,
175317cec90SSong Liu#                  and one which becomes permanent is a fixture that quietly
176317cec90SSong Liu#                  stopped testing anything.
177317cec90SSong Liu#
178317cec90SSong Liu# A bare skip() is neither, and the runner counts it as a failure: a test which
179317cec90SSong Liu# gives up for a reason it never declared is a hole, not an outcome.
180*9b11acaaSJoe Lawrencedeclared_skip()
181*9b11acaaSJoe Lawrence{
182*9b11acaaSJoe Lawrence	KLP_TEST_REPORTED=1
183*9b11acaaSJoe Lawrence	echo "ok - $test_name # SKIP (declared) $*"
184*9b11acaaSJoe Lawrence	exit 0
185*9b11acaaSJoe Lawrence}
186*9b11acaaSJoe Lawrence
187*9b11acaaSJoe Lawrenceprobe_skip()
188*9b11acaaSJoe Lawrence{
189*9b11acaaSJoe Lawrence	KLP_TEST_REPORTED=1
190*9b11acaaSJoe Lawrence	echo "ok - $test_name # SKIP (probe) $*"
191*9b11acaaSJoe Lawrence	exit 0
192*9b11acaaSJoe Lawrence}
193317cec90SSong Liuskip()          { echo "ok - $test_name # SKIP $*"; exit 0; }
194317cec90SSong Liu
195317cec90SSong Liu# TAP directives.  A test which is known to fail reports it rather than being
196317cec90SSong Liu# commented out and forgotten, and one which starts passing again says so
197317cec90SSong Liu# instead of quietly going green: the expectation has to be removed by hand,
198317cec90SSong Liu# which is the point.
199317cec90SSong Liuxfail()
200317cec90SSong Liu{
201*9b11acaaSJoe Lawrence	KLP_TEST_REPORTED=1
202317cec90SSong Liu	echo "not ok - $test_name${1:+: $1} # TODO known failure"
203317cec90SSong Liu	exit 0
204317cec90SSong Liu}
205317cec90SSong Liu
206317cec90SSong Liuxpass()
207317cec90SSong Liu{
208*9b11acaaSJoe Lawrence	KLP_TEST_FAILED=1
209317cec90SSong Liu	echo "ok - $test_name${1:+: $1} # TODO expected failure, but passed"
210317cec90SSong Liu	exit 1
211317cec90SSong Liu}
212cff27d90SPuranjay Mohan
213*9b11acaaSJoe Lawrence# cleanup [exit]
214*9b11acaaSJoe Lawrence#
215*9b11acaaSJoe Lawrence# Called with "exit" from the trap, when the test is over and what it built may
216*9b11acaaSJoe Lawrence# be worth keeping.  Called bare by a test which has finished with one segment
217*9b11acaaSJoe Lawrence# and is about to setup() another: that one is done with, whatever the outcome
218*9b11acaaSJoe Lawrence# of the segments still to come, so it goes.
2198593055bSSong Liucleanup()
2208593055bSSong Liu{
2218593055bSSong Liu	[ -n "$workdir" ] || return 0
2228593055bSSong Liu
223*9b11acaaSJoe Lawrence	# run-tests.sh exports KLP_TEST_KEEP, having validated it; a test run on
224*9b11acaaSJoe Lawrence	# its own reads KEEP itself, so the same setting means the same thing
225*9b11acaaSJoe Lawrence	# either way.
226*9b11acaaSJoe Lawrence	case "${KLP_TEST_KEEP:-${KEEP:-failed}}" in
227*9b11acaaSJoe Lawrence	all)	return 0 ;;
228*9b11acaaSJoe Lawrence	none)	rm -rf "$workdir" ;;
229*9b11acaaSJoe Lawrence	failed|*)
230*9b11acaaSJoe Lawrence		[ "${1:-}" = exit ] || {
2318593055bSSong Liu			rm -rf "$workdir"
232*9b11acaaSJoe Lawrence			return 0
233*9b11acaaSJoe Lawrence		}
234*9b11acaaSJoe Lawrence		# Keep what the runner is going to point at.  It counts as a
235*9b11acaaSJoe Lawrence		# failure anything which did not report an expected outcome --
236*9b11acaaSJoe Lawrence		# including a test which died before printing one, and an
237*9b11acaaSJoe Lawrence		# undeclared skip -- and none of those set KLP_TEST_FAILED, so
238*9b11acaaSJoe Lawrence		# the question to ask is whether a result was reported at all.
239*9b11acaaSJoe Lawrence		# An exit status cannot answer it: a test killed by a signal
240*9b11acaaSJoe Lawrence		# runs this trap with the status of whatever ran last.
241*9b11acaaSJoe Lawrence		[ -n "${KLP_TEST_REPORTED:-}" ] && [ -z "${KLP_TEST_FAILED:-}" ] && {
242*9b11acaaSJoe Lawrence			rm -rf "$workdir"
243*9b11acaaSJoe Lawrence			return 0
244*9b11acaaSJoe Lawrence		}
245*9b11acaaSJoe Lawrence		# run on its own there is no runner to say where it was kept
246*9b11acaaSJoe Lawrence		[ -n "${KLP_TEST_WORKDIR:-}" ] ||
247*9b11acaaSJoe Lawrence			echo "# kept $workdir"
248*9b11acaaSJoe Lawrence		;;
249*9b11acaaSJoe Lawrence	esac
2508593055bSSong Liu}
251cff27d90SPuranjay Mohan
252cff27d90SPuranjay Mohan# setup [exported symbol...]
253cff27d90SPuranjay Mohansetup()
254cff27d90SPuranjay Mohan{
2558593055bSSong Liu	if [ -n "${KLP_TEST_WORKDIR:-}" ]; then
2568593055bSSong Liu		workdir="$KLP_TEST_WORKDIR"
2578593055bSSong Liu		mkdir -p "$workdir" || fail "cannot create $workdir"
2588593055bSSong Liu	else
259cff27d90SPuranjay Mohan		workdir="$(mktemp -d)" || fail "mktemp failed"
2608593055bSSong Liu	fi
261*9b11acaaSJoe Lawrence	trap 'cleanup exit' EXIT
262cff27d90SPuranjay Mohan
263cff27d90SPuranjay Mohan	export_syms "$@"
264cff27d90SPuranjay Mohan}
265cff27d90SPuranjay Mohan
266cff27d90SPuranjay Mohan# export_syms [symbol...]
267cff27d90SPuranjay Mohan#
268cff27d90SPuranjay Mohan# Rewrite Module.symvers so exactly these symbols are exported by vmlinux.
269cff27d90SPuranjay Mohan# Whether a symbol is listed decides between an ordinary relocation and a klp
270cff27d90SPuranjay Mohan# relocation, so tests flip it to cover both.
271cff27d90SPuranjay Mohanexport_syms()
272cff27d90SPuranjay Mohan{
273cff27d90SPuranjay Mohan	: > "$workdir/Module.symvers"
2742ab0faebSSong Liu	add_exports vmlinux "$@"
2752ab0faebSSong Liu}
2762ab0faebSSong Liu
2772ab0faebSSong Liu# add_exports <object> [symbol...]
2782ab0faebSSong Liu#
2792ab0faebSSong Liu# Append exports owned by one object, without clearing what is already there,
2802ab0faebSSong Liu# so a test can describe a kernel where several objects export things.
2812ab0faebSSong Liu#
2822ab0faebSSong Liu# Which object owns a symbol is not cosmetic: a reference to a vmlinux symbol
2832ab0faebSSong Liu# is applied when the patch module loads, and a reference to a module's symbol
2842ab0faebSSong Liu# when that patched module loads, so klp diff files them in different sections.
2852ab0faebSSong Liuadd_exports()
2862ab0faebSSong Liu{
2872ab0faebSSong Liu	local owner="$1"; shift
2882ab0faebSSong Liu
2892ab0faebSSong Liu	add_exports_ns "$owner" "" "$@"
2902ab0faebSSong Liu}
2912ab0faebSSong Liu
2922ab0faebSSong Liu# add_exports_ns <object> <namespace> [symbol...]
2932ab0faebSSong Liu#
2942ab0faebSSong Liu# Exports in a symbol namespace, the last field of a Module.symvers line.
2952ab0faebSSong Liu#
2962ab0faebSSong Liu# A "module:<names>" namespace is EXPORT_SYMBOL_FOR_MODULES(), where the module
2972ab0faebSSong Liu# loader grants access by matching the importing module's name against the
2982ab0faebSSong Liu# list.  A livepatch module is never on that list, so such a symbol has to be
2992ab0faebSSong Liu# referenced the way an unexported one is.  Ordinary namespaces are not
3002ab0faebSSong Liu# special here.
3012ab0faebSSong Liuadd_exports_ns()
3022ab0faebSSong Liu{
3032ab0faebSSong Liu	local owner="$1" ns="$2"; shift 2
3042ab0faebSSong Liu
3052ab0faebSSong Liu	local sym
3062ab0faebSSong Liu
307cff27d90SPuranjay Mohan	for sym in "$@"; do
3082ab0faebSSong Liu		printf '0x00000000\t%s\t%s\tEXPORT_SYMBOL\t%s\n' \
3092ab0faebSSong Liu			"$sym" "$owner" "$ns" >> "$workdir/Module.symvers"
310cff27d90SPuranjay Mohan	done
311cff27d90SPuranjay Mohan}
312cff27d90SPuranjay Mohan
313317cec90SSong Liu# gcc_only / clang_only <reason>
314317cec90SSong Liugcc_only()
315317cec90SSong Liu{
316317cec90SSong Liu	case "$($CC --version 2>/dev/null | head -1)" in
317317cec90SSong Liu	*[Gg][Cc][Cc]*)	return 0 ;;
318317cec90SSong Liu	esac
319317cec90SSong Liu	declared_skip "gcc only${1:+: $1}"
320317cec90SSong Liu}
321317cec90SSong Liu
322317cec90SSong Liuclang_only()
323317cec90SSong Liu{
324317cec90SSong Liu	case "$($CC --version 2>/dev/null | head -1)" in
325317cec90SSong Liu	*clang*)	return 0 ;;
326317cec90SSong Liu	esac
327317cec90SSong Liu	declared_skip "clang only${1:+: $1}"
328317cec90SSong Liu}
329317cec90SSong Liu
330ae04b6d0SSong Liu# build_one <fixture.c> <output object> [cflags...]
331ae04b6d0SSong Liubuild_one()
332cff27d90SPuranjay Mohan{
333ae04b6d0SSong Liu	local fixture out
334ae04b6d0SSong Liu	fixture="$FIXTURES_DIR/$1"
335ae04b6d0SSong Liu	out="$workdir/$2"
336ae04b6d0SSong Liu	shift 2
337cff27d90SPuranjay Mohan
338cff27d90SPuranjay Mohan	[ -f "$fixture" ] || fail "missing fixture $fixture"
339cff27d90SPuranjay Mohan
340ae04b6d0SSong Liu	# run_checksum only runs once per workdir.  A fresh object has no
341ae04b6d0SSong Liu	# checksums in it, so anything built now needs that to happen again.
342ae04b6d0SSong Liu	rm -f "$workdir/.checksummed"
343ae04b6d0SSong Liu
344ae04b6d0SSong Liu	$CC -c $FIXTURE_CFLAGS "$@" -o "$out" "$fixture" 2>"$workdir/cc.log" ||
345ae04b6d0SSong Liu		fail "$(basename "$fixture") does not build: $(tail -1 "$workdir/cc.log")"
346ae04b6d0SSong Liu}
347ae04b6d0SSong Liu
348ae04b6d0SSong Liu# build_pair <fixture.c> [cflags...]
349ae04b6d0SSong Liubuild_pair()
350ae04b6d0SSong Liu{
351ae04b6d0SSong Liu	local fixture="$1"; shift
352ae04b6d0SSong Liu
353ae04b6d0SSong Liu	# Name what this builds.  A test may run several segments, and
354ae04b6d0SSong Liu	# make_vmlinux_pair() repoints orig_obj at the image it links, so
355ae04b6d0SSong Liu	# without this the next run_diff() would still be reading that.
356ae04b6d0SSong Liu	orig_obj=orig.o
357ae04b6d0SSong Liu	patched_obj=patched.o
358ae04b6d0SSong Liu
359ae04b6d0SSong Liu	build_one "$fixture" orig.o "$@"
360ae04b6d0SSong Liu	build_one "$fixture" patched.o "$@" -DPATCHED
361ae04b6d0SSong Liu}
362ae04b6d0SSong Liu
363ae04b6d0SSong Liu# run_objtool_check <objtool arguments...>
364ae04b6d0SSong Liu#
365ae04b6d0SSong Liu# Run objtool's ordinary check pass over the pair, as the kernel build does.
366ae04b6d0SSong Liu#
367ae04b6d0SSong Liu# Some of what klp diff consumes is produced by this pass rather than by the
368ae04b6d0SSong Liu# compiler: .static_call_sites, .mcount_loc, .ibt_endbr_seal, ORC.
369ae04b6d0SSong Liu#
370ae04b6d0SSong Liu# Only module objects see it before klp-build -- with CONFIG_KLP_BUILD the
371ae04b6d0SSong Liu# per-object pass is deferred, so built-in objects reach klp diff exactly as
372ae04b6d0SSong Liu# the compiler left them.
373ae04b6d0SSong Liurun_objtool_check()
374ae04b6d0SSong Liu{
375ae04b6d0SSong Liu	local obj
376ae04b6d0SSong Liu
377ae04b6d0SSong Liu	# This rewrites both objects, so checksums taken before it describe
378ae04b6d0SSong Liu	# something that no longer exists.  As in build_one(), drop the marker
379ae04b6d0SSong Liu	# so run_checksum() takes them again.
380ae04b6d0SSong Liu	rm -f "$workdir/.checksummed"
381ae04b6d0SSong Liu
382ae04b6d0SSong Liu	for obj in "$orig_obj" "$patched_obj"; do
383ae04b6d0SSong Liu		"$OBJTOOL" "$@" "$workdir/$obj" ||
384ae04b6d0SSong Liu			fail "objtool $* failed on $obj"
385ae04b6d0SSong Liu	done
386ae04b6d0SSong Liu}
387ae04b6d0SSong Liu
388ae04b6d0SSong Liurun_checksum()
389ae04b6d0SSong Liu{
390ae04b6d0SSong Liu	# Checksums live in the objects, and a test may ask for them more than
391ae04b6d0SSong Liu	# once -- diffing the same pair again with a different Module.symvers,
392ae04b6d0SSong Liu	# say.  objtool does the right thing when asked twice, leaving the
393ae04b6d0SSong Liu	# object alone, but it says so, and that warning would be most of what
394ae04b6d0SSong Liu	# a passing run prints.  Remember instead, and keep quiet.
395ae04b6d0SSong Liu	[ -e "$workdir/.checksummed" ] && return 0
396ae04b6d0SSong Liu
397ae04b6d0SSong Liu	"$OBJTOOL" klp checksum "$workdir/$orig_obj" ||
398ae04b6d0SSong Liu		fail "klp checksum $orig_obj failed"
399ae04b6d0SSong Liu	"$OBJTOOL" klp checksum "$workdir/$patched_obj" ||
400ae04b6d0SSong Liu		fail "klp checksum $patched_obj failed"
401ae04b6d0SSong Liu	touch "$workdir/.checksummed"
402cff27d90SPuranjay Mohan}
403cff27d90SPuranjay Mohan
404cff27d90SPuranjay Mohan# run_diff [expected exit status]
405cff27d90SPuranjay Mohanrun_diff()
406cff27d90SPuranjay Mohan{
407cff27d90SPuranjay Mohan	local expect="${1:-0}" rc=0
408cff27d90SPuranjay Mohan
409ae04b6d0SSong Liu	run_checksum
410cff27d90SPuranjay Mohan
411cff27d90SPuranjay Mohan	# klp diff looks for Module.symvers relative to the working directory.
412ae04b6d0SSong Liu	( cd "$workdir" && "$OBJTOOL" klp diff "$orig_obj" "$patched_obj" out.o ) \
413cff27d90SPuranjay Mohan		> "$workdir/diff.log" 2>&1 || rc=$?
414cff27d90SPuranjay Mohan
415cff27d90SPuranjay Mohan	[ "$rc" = "$expect" ] ||
416cff27d90SPuranjay Mohan		fail "klp diff exited $rc, expected $expect: $(tail -2 "$workdir/diff.log")"
417cff27d90SPuranjay Mohan}
418cff27d90SPuranjay Mohan
419cff27d90SPuranjay Mohancc_supports()
420cff27d90SPuranjay Mohan{
421cff27d90SPuranjay Mohan	echo 'int f(void) { return 0; }' > "$workdir/flagtest.c"
422cff27d90SPuranjay Mohan	$CC $1 -c "$workdir/flagtest.c" -o "$workdir/flagtest.o" 2>/dev/null
423cff27d90SPuranjay Mohan}
424cff27d90SPuranjay Mohan
425cff27d90SPuranjay Mohan# partial_link <output> <object...>
426cff27d90SPuranjay Mohan#
427cff27d90SPuranjay Mohan# "ld -r" through the compiler driver so the link targets the same
428cff27d90SPuranjay Mohan# architecture as the objects.
429cff27d90SPuranjay Mohanpartial_link()
430cff27d90SPuranjay Mohan{
431cff27d90SPuranjay Mohan	local out="$1"; shift
432cff27d90SPuranjay Mohan
433ae04b6d0SSong Liu	rm -f "$workdir/.checksummed"
434ae04b6d0SSong Liu
435cff27d90SPuranjay Mohan	$CC -r -nostdlib -o "$out" "$@" 2>/dev/null ||
436cff27d90SPuranjay Mohan		$CC -r -nostdlib -fuse-ld=lld -o "$out" "$@" 2>/dev/null
437cff27d90SPuranjay Mohan}
438cff27d90SPuranjay Mohan
439ae04b6d0SSong Liu# link_vmlinux <output> <object...>
440ae04b6d0SSong Liu#
441ae04b6d0SSong Liu# Link objects into an executable, the way the kernel's final link produces
442ae04b6d0SSong Liu# vmlinux from vmlinux.o.  Entry point 0 and no libc: nothing runs it, it only
443ae04b6d0SSong Liu# has to be a linked image with resolved addresses.
444ae04b6d0SSong Liu#
445ae04b6d0SSong Liu# The sub-sections have to come out in name order rather than object order,
446ae04b6d0SSong Liu# the way the kernel's linker script gathers .text.unlikely and .data.. apart
447ae04b6d0SSong Liu# from the rest.  That reordering is the entire reason .klp.symid exists: a
448ae04b6d0SSong Liu# link which preserves order cannot tell a correct sympos from one that merely
449ae04b6d0SSong Liu# counted, and the caller checks the two orders really did diverge.
450ae04b6d0SSong Liu#
451ae04b6d0SSong Liu# A linker script rather than --sort-section=name, because lld accepts that
452ae04b6d0SSong Liu# option and ignores it -- so on a host where only lld can link the target, the
453ae04b6d0SSong Liu# test would quietly stop testing the thing it is named for.
454ae04b6d0SSong Liu#
455ae04b6d0SSong Liu# Three attempts because a cross run has neither $LD nor the compiler's default
456ae04b6d0SSong Liu# linker able to touch the target: on an arm64 host linking x86 objects, only
457ae04b6d0SSong Liu# lld will do it.
458ae04b6d0SSong Liulink_vmlinux()
459ae04b6d0SSong Liu{
460ae04b6d0SSong Liu	local out="$1" lds="$workdir/sort.lds"; shift
461ae04b6d0SSong Liu
462ae04b6d0SSong Liu	echo 'SECTIONS { .data : { *(SORT_BY_NAME(.data.*)) } }' > "$lds"
463ae04b6d0SSong Liu
464ae04b6d0SSong Liu	$LD -e 0 -T "$lds" -o "$out" "$@" 2>/dev/null ||
465ae04b6d0SSong Liu		$CC -nostdlib -Wl,-e,0 -Wl,-T,"$lds" \
466ae04b6d0SSong Liu			-o "$out" "$@" 2>/dev/null ||
467ae04b6d0SSong Liu		$CC -nostdlib -fuse-ld=lld -Wl,-e,0 -Wl,-T,"$lds" \
468ae04b6d0SSong Liu			-o "$out" "$@" 2>/dev/null
469ae04b6d0SSong Liu}
470ae04b6d0SSong Liu
471ae04b6d0SSong Liu# make_vmlinux_pair <orig object...> -- <patched object...>
472ae04b6d0SSong Liu#
473ae04b6d0SSong Liu# Build the vmlinux.o / vmlinux pair klp diff needs to resolve sympos the way
474ae04b6d0SSong Liu# it does for built-in code, and point the diff at it.
475ae04b6d0SSong Liu#
476ae04b6d0SSong Liu# For a module, sympos is a count in symbol table order, which klp diff can do
477ae04b6d0SSong Liu# from the object alone.  vmlinux is different: the final link reorders
478ae04b6d0SSong Liu# sub-sections, so the position comes from the linked image, bridged by
479ae04b6d0SSong Liu# .klp.symid.  klp diff only looks for that when the object it was handed is
480ae04b6d0SSong Liu# called vmlinux.o and a vmlinux sits beside it -- so both the name and the
481ae04b6d0SSong Liu# linked image matter.
482ae04b6d0SSong Liumake_vmlinux_pair()
483ae04b6d0SSong Liu{
484ae04b6d0SSong Liu	local orig=() patched=() seen= arg
485ae04b6d0SSong Liu
486ae04b6d0SSong Liu	for arg in "$@"; do
487ae04b6d0SSong Liu		if [ "$arg" = -- ]; then seen=y; continue; fi
488ae04b6d0SSong Liu		if [ -n "$seen" ]; then patched+=( "$arg" ); else orig+=( "$arg" ); fi
489ae04b6d0SSong Liu	done
490ae04b6d0SSong Liu
491ae04b6d0SSong Liu	# Both sides have to have been named.  Without this, forgetting the --
492ae04b6d0SSong Liu	# leaves one list empty, the link of nothing fails, and the test skips
493ae04b6d0SSong Liu	# saying the toolchain cannot link -- which is a test bug wearing the
494ae04b6d0SSong Liu	# costume of an environment one.
495ae04b6d0SSong Liu	[ "${#orig[@]}" -gt 0 ] && [ "${#patched[@]}" -gt 0 ] ||
496ae04b6d0SSong Liu		fail "make_vmlinux_pair needs objects either side of --"
497ae04b6d0SSong Liu
498ae04b6d0SSong Liu	partial_link "$workdir/vmlinux.o" "${orig[@]}" ||
499ae04b6d0SSong Liu		probe_skip "partial link unavailable"
500ae04b6d0SSong Liu	partial_link "$workdir/patched.o" "${patched[@]}" ||
501ae04b6d0SSong Liu		probe_skip "partial link unavailable"
502ae04b6d0SSong Liu
503ae04b6d0SSong Liu	"$OBJTOOL" --klp-symids --link "$workdir/vmlinux.o" ||
504ae04b6d0SSong Liu		fail "objtool --klp-symids failed"
505ae04b6d0SSong Liu
506ae04b6d0SSong Liu	link_vmlinux "$workdir/vmlinux" "$workdir/vmlinux.o" ||
507ae04b6d0SSong Liu		probe_skip "cannot link a vmlinux here"
508ae04b6d0SSong Liu
509ae04b6d0SSong Liu	orig_obj=vmlinux.o
510ae04b6d0SSong Liu}
511ae04b6d0SSong Liu
512ae04b6d0SSong Liu# build_module_pair <fixture.c> <module name> [cflags...]
513ae04b6d0SSong Liu#
514ae04b6d0SSong Liu# Build the pair as objects belonging to a module rather than to vmlinux.  klp
515ae04b6d0SSong Liu# diff reads the object's module name from .modinfo, and that decides which
516ae04b6d0SSong Liu# object a relocation is attributed to and whether a reference counts as
517ae04b6d0SSong Liu# cross-module, so a good deal of the code has a module path the vmlinux
518ae04b6d0SSong Liu# fixtures never reach.
519ae04b6d0SSong Liu#
520ae04b6d0SSong Liu# The fixture defines its .modinfo name from MODNAME.  Passing that through
521ae04b6d0SSong Liu# -D needs two levels of quoting, which is easy to get wrong at the call site.
522ae04b6d0SSong Liubuild_module_pair()
523ae04b6d0SSong Liu{
524ae04b6d0SSong Liu	local fixture="$1" modname="$2"; shift 2
525ae04b6d0SSong Liu
526ae04b6d0SSong Liu	build_pair "$fixture" -DMODNAME="\"$modname\"" "$@"
527ae04b6d0SSong Liu}
528ae04b6d0SSong Liu
529cff27d90SPuranjay Mohan# find_thinlto_toolchain
530cff27d90SPuranjay Mohan#
531317cec90SSong Liu# Set $THIN_LD to an lld from the same LLVM release as $CC (or THIN_CC).  A
532cff27d90SPuranjay Mohan# mismatched pair fails with "Invalid summary version", which reads like a
533cff27d90SPuranjay Mohan# broken test rather than a broken environment.
534317cec90SSong Liu#
535317cec90SSong Liu# ThinLTO is clang-only; callers must use clang_only before calling this.
536317cec90SSong Liu# Only $CC (or an explicit THIN_CC override) is consulted -- the harness does
537317cec90SSong Liu# not search for a second compiler beside a gcc $CC.
538cff27d90SPuranjay Mohanfind_thinlto_toolchain()
539cff27d90SPuranjay Mohan{
540317cec90SSong Liu	local cc ver ld
541cff27d90SPuranjay Mohan
542317cec90SSong Liu	for cc in "${THIN_CC:-}" "$CC"; do
543cff27d90SPuranjay Mohan		[ -n "$cc" ] || continue
544317cec90SSong Liu		command -v "${cc%% *}" >/dev/null 2>&1 || return 1
545cff27d90SPuranjay Mohan
546cff27d90SPuranjay Mohan		ver=$($cc -dumpversion 2>/dev/null | cut -d. -f1)
547cff27d90SPuranjay Mohan
548cff27d90SPuranjay Mohan		for ld in "${THIN_LD:-}" "ld.lld-$ver" ld.lld; do
549cff27d90SPuranjay Mohan			[ -n "$ld" ] || continue
550cff27d90SPuranjay Mohan			command -v "$ld" >/dev/null 2>&1 || continue
551cff27d90SPuranjay Mohan
552cff27d90SPuranjay Mohan			echo 'int probe(void) { return 0; }' > "$workdir/probe.c"
553cff27d90SPuranjay Mohan			$cc -flto=thin -O2 -c "$workdir/probe.c" \
554cff27d90SPuranjay Mohan				-o "$workdir/probe.o" 2>/dev/null || continue
555cff27d90SPuranjay Mohan			"$ld" -r "$workdir/probe.o" -o "$workdir/probe.elf" \
556cff27d90SPuranjay Mohan				2>/dev/null || continue
557cff27d90SPuranjay Mohan
558cff27d90SPuranjay Mohan			THIN_CC="$cc"
559cff27d90SPuranjay Mohan			THIN_LD="$ld"
560cff27d90SPuranjay Mohan			return 0
561cff27d90SPuranjay Mohan		done
562cff27d90SPuranjay Mohan	done
563cff27d90SPuranjay Mohan
564cff27d90SPuranjay Mohan	return 1
565cff27d90SPuranjay Mohan}
566cff27d90SPuranjay Mohan
567c009fabcSSong Liuout_sections() { $READELF -S -W "$workdir/out.o" 2>/dev/null; }
568c009fabcSSong Liuout_relocs()   { $READELF -r -W "$workdir/out.o" 2>/dev/null; }
569c009fabcSSong Liuout_symbols()  { $READELF -s -W "$workdir/out.o" 2>/dev/null; }
570cff27d90SPuranjay Mohandiff_log()     { cat "$workdir/diff.log"; }
571cff27d90SPuranjay Mohan
5722ab0faebSSong Liu# out_strings <section>
5732ab0faebSSong Liu#
5742ab0faebSSong Liu# The strings in one section of the output, for the names livepatch matches on.
5752ab0faebSSong Liuout_strings() { $READELF -p "$1" "$workdir/out.o" 2>/dev/null; }
5762ab0faebSSong Liu
5772ab0faebSSong Liu# Checks on the input objects, to run before klp diff.  The two forms differ in
5782ab0faebSSong Liu# what an absent construct means:
5792ab0faebSSong Liu#
5802ab0faebSSong Liu#   require_*  the compiler cannot produce it here	-> skip
5812ab0faebSSong Liu#   assert_*   the fixture is supposed to produce it	-> fail
5822ab0faebSSong Liu
5832ab0faebSSong Liuin_sections() { $READELF -S -W "$workdir/$1" 2>/dev/null; }
5842ab0faebSSong Liuin_symbols()  { $READELF -s -W "$workdir/$1" 2>/dev/null; }
5852ab0faebSSong Liuin_relocs()   { $READELF -r -W "$workdir/$1" 2>/dev/null; }
5862ab0faebSSong Liu
5872ab0faebSSong Liu# count_input_symbols <object> <name>
5882ab0faebSSong Liu#
5892ab0faebSSong Liu# How many object symbols of exactly that name the input has.  Deliberately not
5902ab0faebSSong Liu# a grep: readelf lists section symbols too, and a newer binutils prints their
5912ab0faebSSong Liu# name -- ".data.<name>" -- where an older one leaves the column blank.  A dot
5922ab0faebSSong Liu# is not a word character, so "grep -w <name>" counts that line as well, and
5932ab0faebSSong Liu# the same object gives a different answer depending on which readelf reads it.
5942ab0faebSSong Liucount_input_symbols()
5952ab0faebSSong Liu{
5962ab0faebSSong Liu	in_symbols "$1" | awk -v n="$2" '$4 == "OBJECT" && $8 == n' | wc -l
5972ab0faebSSong Liu}
5982ab0faebSSong Liu
5992ab0faebSSong Liu# re_quote <string>
6002ab0faebSSong Liu#
6012ab0faebSSong Liu# A string as a literal basic regular expression.  Nearly every name these
6022ab0faebSSong Liu# assertions match on contains a dot -- .text.target, .klp.rela.vmlinux -- and
6032ab0faebSSong Liu# an unescaped dot matches any character, so an assertion for one section can be
6042ab0faebSSong Liu# satisfied by a different one whose name merely lines up.
6052ab0faebSSong Liure_quote() { printf '%s' "$1" | sed 's|[].[^$*\\/]|\\&|g'; }
6062ab0faebSSong Liu
6072ab0faebSSong Liuhas_input_section() { in_sections "$1" | grep -q "[[:space:]]$(re_quote "$2")[[:space:]]"; }
6082ab0faebSSong Liuhas_input_symbol()  { in_symbols "$1" | awk -v n="$2" '$NF == n' | grep -q .; }
6092ab0faebSSong Liu
6102ab0faebSSong Liuassert_input_section()
6112ab0faebSSong Liu{
6122ab0faebSSong Liu	local obj
6132ab0faebSSong Liu
6142ab0faebSSong Liu	for obj in "$orig_obj" "$patched_obj"; do
6152ab0faebSSong Liu		has_input_section "$obj" "$1" ||
6162ab0faebSSong Liu			fail "fixture produced no section '$1' in $obj"
6172ab0faebSSong Liu	done
6182ab0faebSSong Liu}
6192ab0faebSSong Liu
6202ab0faebSSong Liuassert_input_symbol()
6212ab0faebSSong Liu{
6222ab0faebSSong Liu	local obj
6232ab0faebSSong Liu
6242ab0faebSSong Liu	for obj in "$orig_obj" "$patched_obj"; do
6252ab0faebSSong Liu		has_input_symbol "$obj" "$1" ||
6262ab0faebSSong Liu			fail "fixture produced no symbol '$1' in $obj"
6272ab0faebSSong Liu	done
6282ab0faebSSong Liu}
6292ab0faebSSong Liu
6302ab0faebSSong Liurequire_input_section()
6312ab0faebSSong Liu{
6322ab0faebSSong Liu	local obj
6332ab0faebSSong Liu
6342ab0faebSSong Liu	for obj in "$orig_obj" "$patched_obj"; do
6352ab0faebSSong Liu		has_input_section "$obj" "$1" ||
6362ab0faebSSong Liu			probe_skip "compiler produced no section '$1' here"
6372ab0faebSSong Liu	done
6382ab0faebSSong Liu}
6392ab0faebSSong Liu
640cff27d90SPuranjay Mohanassert_section()
641cff27d90SPuranjay Mohan{
6422ab0faebSSong Liu	out_sections | grep -q "[[:space:]]$(re_quote "$1")[[:space:]]" ||
643cff27d90SPuranjay Mohan		fail "expected section '$1' in output"
644cff27d90SPuranjay Mohan}
645cff27d90SPuranjay Mohan
6462ab0faebSSong Liuassert_no_section()
6472ab0faebSSong Liu{
6482ab0faebSSong Liu	out_sections | grep -q "[[:space:]]$(re_quote "$1")[[:space:]]" &&
6492ab0faebSSong Liu		fail "unexpected section '$1' in output"
6502ab0faebSSong Liu	return 0
6512ab0faebSSong Liu}
6522ab0faebSSong Liu
653cff27d90SPuranjay Mohanassert_patched()
654cff27d90SPuranjay Mohan{
655cff27d90SPuranjay Mohan	assert_section ".text.$1"
656cff27d90SPuranjay Mohan}
657cff27d90SPuranjay Mohan
658cff27d90SPuranjay Mohanassert_not_patched()
659cff27d90SPuranjay Mohan{
6602ab0faebSSong Liu	out_sections | grep -q "[[:space:]]$(re_quote ".text.$1")[[:space:]]" &&
661cff27d90SPuranjay Mohan		fail "function '$1' should not have been cloned"
662cff27d90SPuranjay Mohan	return 0
663cff27d90SPuranjay Mohan}
6642ab0faebSSong Liu
6652ab0faebSSong Liu# section_relocs <section>
6662ab0faebSSong Liu#
6672ab0faebSSong Liu# The relocations against one section.  readelf prints every relocation section
6682ab0faebSSong Liu# in turn, so a test asking about ".smp_locks" has to cut its block out of the
6692ab0faebSSong Liu# listing first.
6702ab0faebSSong Liusection_relocs()
6712ab0faebSSong Liu{
6722ab0faebSSong Liu	local sec="${1//./\\.}"
6732ab0faebSSong Liu
6742ab0faebSSong Liu	out_relocs | awk "/rela$sec'/,/^\$/"
6752ab0faebSSong Liu}
6762ab0faebSSong Liu
6772ab0faebSSong Liuassert_reloc_sym()
6782ab0faebSSong Liu{
6792ab0faebSSong Liu	section_relocs "$1" | awk -v n="$2" '$5 == n' | grep -q . ||
6802ab0faebSSong Liu		fail "expected a relocation to '$2' in '$1'"
6812ab0faebSSong Liu}
6822ab0faebSSong Liu
6832ab0faebSSong Liuassert_no_reloc_sym()
6842ab0faebSSong Liu{
6852ab0faebSSong Liu	section_relocs "$1" | awk -v n="$2" '$5 == n' | grep -q . &&
6862ab0faebSSong Liu		fail "unexpected relocation to '$2' in '$1'"
6872ab0faebSSong Liu	return 0
6882ab0faebSSong Liu}
6892ab0faebSSong Liu
6902ab0faebSSong Liu# assert_reloc_count <section> <count>
6912ab0faebSSong Liu#
6922ab0faebSSong Liu# Counts relocation entries, not header or blank lines: whether a special
6932ab0faebSSong Liu# section entry was extracted once, twice or not at all is usually the whole
6942ab0faebSSong Liu# question.
6952ab0faebSSong Liu#
6962ab0faebSSong Liu# A count of zero is ambiguous on its own -- a section with no relocations and
6972ab0faebSSong Liu# no section at all both read as zero -- so require the section to exist.  A
6982ab0faebSSong Liu# test expecting nothing there wants assert_no_section.
6992ab0faebSSong Liuassert_reloc_count()
7002ab0faebSSong Liu{
7012ab0faebSSong Liu	local n
7022ab0faebSSong Liu
7032ab0faebSSong Liu	assert_section "$1"
7042ab0faebSSong Liu
7052ab0faebSSong Liu	n="$(section_relocs "$1" | grep -cE '^[0-9a-f]{8,}')"
7062ab0faebSSong Liu	[ "$n" = "$2" ] ||
7072ab0faebSSong Liu		fail "expected $2 relocations in '$1', found $n"
7082ab0faebSSong Liu}
7092ab0faebSSong Liu
7102ab0faebSSong Liu# assert_klp_sym <symbol> [object]
7112ab0faebSSong Liu#
7122ab0faebSSong Liu# A klp symbol is named .klp.sym.<object>.<symbol>,<sympos>.  The object
7132ab0faebSSong Liu# defaults to any, since most tests care that the reference was converted at
7142ab0faebSSong Liu# all rather than which object it resolved against.
7152ab0faebSSong Liuassert_klp_sym()
7162ab0faebSSong Liu{
7172ab0faebSSong Liu	out_symbols | grep -q "\.klp\.sym\.${2:-[^.]*}\.$(re_quote "$1")," ||
7182ab0faebSSong Liu		fail "expected klp symbol for '$1'"
7192ab0faebSSong Liu}
7202ab0faebSSong Liu
7212ab0faebSSong Liu# assert_klp_sympos <symbol> <sympos>
7222ab0faebSSong Liu#
7232ab0faebSSong Liu# The number after the comma in .klp.sym.<object>.<symbol>,<sympos> says which
7242ab0faebSSong Liu# of several same-named symbols livepatch should resolve to, counting from 1;
7252ab0faebSSong Liu# 0 means the name is unique and no disambiguation is needed.  Resolving to the
7262ab0faebSSong Liu# wrong one is not a load failure, it is a patch quietly wired to the wrong
7272ab0faebSSong Liu# object.
7282ab0faebSSong Liuassert_klp_sympos()
7292ab0faebSSong Liu{
7302ab0faebSSong Liu	out_symbols | grep -qE "\.klp\.sym\.[^.]+\.$(re_quote "$1"),$2([[:space:]]|\$)" ||
7312ab0faebSSong Liu		fail "expected klp symbol for '$1' with sympos $2, found:$(
7322ab0faebSSong Liu			out_symbols | grep -o "\.klp\.sym\.[^.]*\.$(re_quote "$1"),[0-9]*" |
7332ab0faebSSong Liu			sort -u | tr '\n' ' ')"
7342ab0faebSSong Liu}
7352ab0faebSSong Liu
7362ab0faebSSong Liuassert_no_klp_sym()
7372ab0faebSSong Liu{
7382ab0faebSSong Liu	out_symbols | grep -q "\.klp\.sym\.${2:-[^.]*}\.$(re_quote "$1")," &&
7392ab0faebSSong Liu		fail "unexpected klp symbol for '$1'"
7402ab0faebSSong Liu	return 0
7412ab0faebSSong Liu}
7422ab0faebSSong Liu
7432ab0faebSSong Liuassert_tombstone()
7442ab0faebSSong Liu{
7452ab0faebSSong Liu	out_symbols | grep -qE "\.klp\.tombstone\.$(re_quote "$1")([[:space:]]|\$)" ||
7462ab0faebSSong Liu		fail "expected a tombstone for '$1'"
7472ab0faebSSong Liu}
7482ab0faebSSong Liu
7492ab0faebSSong Liuassert_symbol()
7502ab0faebSSong Liu{
7512ab0faebSSong Liu	out_symbols | awk -v n="$1" '$NF == n' | grep -q . ||
7522ab0faebSSong Liu		fail "expected symbol '$1' in output"
7532ab0faebSSong Liu}
7542ab0faebSSong Liu
7552ab0faebSSong Liuassert_no_symbol()
7562ab0faebSSong Liu{
7572ab0faebSSong Liu	out_symbols | awk -v n="$1" '$NF == n' | grep -q . &&
7582ab0faebSSong Liu		fail "unexpected symbol '$1' in output"
7592ab0faebSSong Liu	return 0
7602ab0faebSSong Liu}
7612ab0faebSSong Liu
7622ab0faebSSong Liu# assert_diff_log <regex>
7632ab0faebSSong Liu#
7642ab0faebSSong Liu# klp diff's combined output, for tests asserting on a diagnostic.  Error
7652ab0faebSSong Liu# messages are part of the interface when the whole point is that a construct
7662ab0faebSSong Liu# gets rejected, and a rejection for the wrong reason is not a pass.
7672ab0faebSSong Liuassert_diff_log()
7682ab0faebSSong Liu{
7692ab0faebSSong Liu	diff_log | grep -qE -- "$1" ||
7702ab0faebSSong Liu		fail "expected '$1' in klp diff output: $(tail -2 "$workdir/diff.log")"
7712ab0faebSSong Liu}
7722ab0faebSSong Liu
7732ab0faebSSong Liu# checksum_of <object> <symbol>
7742ab0faebSSong Liu#
7752ab0faebSSong Liu# The checksum "klp checksum" recorded for one symbol, as a hex string.
7762ab0faebSSong Liu#
7772ab0faebSSong Liu# .discard.sym_checksum is an array of { u64 addr; u64 checksum; }, where addr
7782ab0faebSSong Liu# is the target of a relocation naming the symbol.  Nothing in the section
7792ab0faebSSong Liu# itself says which symbol an entry belongs to, so the relocation is what
7802ab0faebSSong Liu# locates the entry; the checksum is the eight bytes after it.
7812ab0faebSSong Liu# Callers use this in a command substitution, where fail() would only exit the
7822ab0faebSSong Liu# subshell and the test would carry on with an empty checksum.  So this returns
7832ab0faebSSong Liu# non-zero and prints nothing, and the assertions below check for that.  For
7842ab0faebSSong Liu# the same reason it does not run run_checksum() itself: that one does call
7852ab0faebSSong Liu# fail(), and from in here the message would be captured as the checksum
7862ab0faebSSong Liu# rather than ending the test.  The caller runs it first.
7872ab0faebSSong Liuchecksum_of()
7882ab0faebSSong Liu{
7892ab0faebSSong Liu	local obj="$workdir/$1" sym="$2" off
7902ab0faebSSong Liu
7912ab0faebSSong Liu	off="$($READELF -rW "$obj" 2>/dev/null |
7922ab0faebSSong Liu	       awk -v s="$sym" '/rela\.discard\.sym_checksum/,/^$/ {
7932ab0faebSSong Liu			if ($5 == s) { print $1; exit }
7942ab0faebSSong Liu	       }')"
7952ab0faebSSong Liu
7962ab0faebSSong Liu	[ -n "$off" ] || return 1
7972ab0faebSSong Liu
7982ab0faebSSong Liu	$OBJCOPY -O binary --only-section=.discard.sym_checksum \
7992ab0faebSSong Liu		"$obj" "$workdir/checksums.bin" 2>/dev/null || return 1
8002ab0faebSSong Liu
8012ab0faebSSong Liu	dd if="$workdir/checksums.bin" bs=1 skip=$((16#$off + 8)) count=8 \
8022ab0faebSSong Liu		status=none | od -An -tx1 | tr -d ' \n'
8032ab0faebSSong Liu}
8042ab0faebSSong Liu
8052ab0faebSSong Liu# assert_checksum_differs <symbol> / assert_checksum_matches <symbol>
8062ab0faebSSong Liu#
8072ab0faebSSong Liu# Compare what klp checksum recorded for a symbol in the original against the
8082ab0faebSSong Liu# patched object.  This is what decides whether klp diff treats a function as
8092ab0faebSSong Liu# changed, so a test asserting only that the right functions were cloned cannot
8102ab0faebSSong Liu# tell a correct checksum from one which happens to differ.
8112ab0faebSSong Liuchecksum_pair()
8122ab0faebSSong Liu{
8132ab0faebSSong Liu	run_checksum
8142ab0faebSSong Liu
8152ab0faebSSong Liu	orig_checksum="$(checksum_of "$orig_obj" "$1")"
8162ab0faebSSong Liu	patched_checksum="$(checksum_of "$patched_obj" "$1")"
8172ab0faebSSong Liu
8182ab0faebSSong Liu	[ -n "$orig_checksum" ] ||
8192ab0faebSSong Liu		fail "no checksum recorded for '$1' in $orig_obj"
8202ab0faebSSong Liu	[ -n "$patched_checksum" ] ||
8212ab0faebSSong Liu		fail "no checksum recorded for '$1' in $patched_obj"
8222ab0faebSSong Liu}
8232ab0faebSSong Liu
8242ab0faebSSong Liuassert_checksum_differs()
8252ab0faebSSong Liu{
8262ab0faebSSong Liu	checksum_pair "$1"
8272ab0faebSSong Liu
8282ab0faebSSong Liu	[ "$orig_checksum" != "$patched_checksum" ] ||
8292ab0faebSSong Liu		fail "checksum for '$1' unchanged at $orig_checksum, expected it to differ"
8302ab0faebSSong Liu}
8312ab0faebSSong Liu
8322ab0faebSSong Liuassert_checksum_matches()
8332ab0faebSSong Liu{
8342ab0faebSSong Liu	checksum_pair "$1"
8352ab0faebSSong Liu
8362ab0faebSSong Liu	[ "$orig_checksum" = "$patched_checksum" ] ||
8372ab0faebSSong Liu		fail "checksum for '$1' changed from $orig_checksum to" \
8382ab0faebSSong Liu		     "$patched_checksum, expected no change"
8392ab0faebSSong Liu}
8402ab0faebSSong Liu
8412ab0faebSSong Liu# run_post_link [expected exit status]
8422ab0faebSSong Liu#
8432ab0faebSSong Liu# klp post-link runs last in a livepatch build, converting the intermediate
8442ab0faebSSong Liu# __klp_relocs.* sections into the .klp.rela.* form the kernel consumes.  It
8452ab0faebSSong Liu# needs nothing but an object containing those sections, which is what klp diff
8462ab0faebSSong Liu# produces, so it runs on out.o here rather than on a built module.  Rewrites
8472ab0faebSSong Liu# out.o in place, so the out_* helpers show the result afterwards.
8482ab0faebSSong Liurun_post_link()
8492ab0faebSSong Liu{
8502ab0faebSSong Liu	local expect="${1:-0}" rc=0
8512ab0faebSSong Liu
8522ab0faebSSong Liu	"$OBJTOOL" klp post-link "$workdir/out.o" \
8532ab0faebSSong Liu		> "$workdir/post-link.log" 2>&1 || rc=$?
8542ab0faebSSong Liu
8552ab0faebSSong Liu	[ "$rc" = "$expect" ] ||
8562ab0faebSSong Liu		fail "klp post-link exited $rc, expected $expect:" \
8572ab0faebSSong Liu		     "$(tail -2 "$workdir/post-link.log")"
8582ab0faebSSong Liu}
8592ab0faebSSong Liu
8602ab0faebSSong Liu# The flags readelf prints for a section, or nothing when it has none.  The
8612ab0faebSSong Liu# leading "[nn]" index is stripped first so the columns can be counted.
8622ab0faebSSong Liusection_flags()
8632ab0faebSSong Liu{
8642ab0faebSSong Liu	out_sections | sed 's/^ *\[[ 0-9]*\] *//' |
8652ab0faebSSong Liu		awk -v s="$1" '$1 == s && $7 ~ /^[A-Za-z]+$/ { print $7 }'
8662ab0faebSSong Liu}
8672ab0faebSSong Liu
8682ab0faebSSong Liu# assert_section_flag <section> <letter>
8692ab0faebSSong Liu#
8702ab0faebSSong Liu# SHF_RELA_LIVEPATCH is OS-specific, so readelf renders it as "o".  A klp rela
8712ab0faebSSong Liu# section which lost it is an ordinary rela section, which the linker may apply
8722ab0faebSSong Liu# and the livepatch code will not.
8732ab0faebSSong Liuassert_section_flag()
8742ab0faebSSong Liu{
8752ab0faebSSong Liu	local flags; flags="$(section_flags "$1")"
8762ab0faebSSong Liu
8772ab0faebSSong Liu	[ -n "$flags" ] ||
8782ab0faebSSong Liu		fail "section '$1' has no flags, expected '$2'"
8792ab0faebSSong Liu	case "$flags" in
8802ab0faebSSong Liu	*"$2"*)	;;
8812ab0faebSSong Liu	*)	fail "section '$1' has flags '$flags', expected '$2'" ;;
8822ab0faebSSong Liu	esac
8832ab0faebSSong Liu}
8842ab0faebSSong Liu
8852ab0faebSSong Liu# assert_klp_rela <object> <section>
8862ab0faebSSong Liu#
8872ab0faebSSong Liu# post-link names the converted sections .klp.rela.<object>.<section>, one per
8882ab0faebSSong Liu# base section.  Also checks SHF_RELA_LIVEPATCH, since the name alone is not
8892ab0faebSSong Liu# what makes the kernel process it.
8902ab0faebSSong Liuassert_klp_rela()
8912ab0faebSSong Liu{
8922ab0faebSSong Liu	local name=".klp.rela.$1.$2"
8932ab0faebSSong Liu
8942ab0faebSSong Liu	out_sections | grep -q "[[:space:]]$(re_quote "$name")[[:space:]]" ||
8952ab0faebSSong Liu		fail "expected section '$name' in output"
8962ab0faebSSong Liu
8972ab0faebSSong Liu	assert_section_flag "$name" o
8982ab0faebSSong Liu}
8992ab0faebSSong Liu
9002ab0faebSSong Liu# assert_livepatch_sym <symbol>
9012ab0faebSSong Liu#
9022ab0faebSSong Liu# Symbols a klp relocation resolves against live in SHN_LIVEPATCH, which
9032ab0faebSSong Liu# readelf prints as "OS [0xff20]" -- llvm-readelf without the space, so match
9042ab0faebSSong Liu# either.  The kernel resolves these itself at patch load; anything else is a
9052ab0faebSSong Liu# symbol the module loader will try, and fail, to resolve normally.
9062ab0faebSSong Liuassert_livepatch_sym()
9072ab0faebSSong Liu{
9082ab0faebSSong Liu	out_symbols | grep -E 'OS ?\[0xff20\]' |
9092ab0faebSSong Liu		grep -qE "\.klp\.sym\.[^.]+\.$(re_quote "$1")," ||
9102ab0faebSSong Liu		fail "expected a klp symbol for '$1' in SHN_LIVEPATCH"
9112ab0faebSSong Liu}
912