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