projects/linux·blob master · refs

911 lines · 29.2 KiB

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