1cff27d90SPuranjay Mohan# SPDX-License-Identifier: GPL-2.0 2cff27d90SPuranjay Mohan# 3cff27d90SPuranjay Mohan# Helpers for the objtool klp tests. A test builds a fixture twice, as the 4cff27d90SPuranjay Mohan# original and (with -DPATCHED) the patched object, runs both through 5cff27d90SPuranjay Mohan# "klp checksum" and diffs them, then asserts on the result. 6cff27d90SPuranjay Mohan# 7cff27d90SPuranjay Mohan# Assertions check properties rather than compare against recorded output: 8cff27d90SPuranjay Mohan# codegen varies between compilers and golden files would report churn instead 9cff27d90SPuranjay Mohan# of regressions. 10cff27d90SPuranjay Mohan 11cff27d90SPuranjay MohanTESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 12d76312b8SSong Liu 13d76312b8SSong Liu# Tests live in generic/ or in an architecture directory beside it, and each 14d76312b8SSong Liu# carries its own fixtures. 15d76312b8SSong LiuFIXTURES_DIR="$(cd "$(dirname "$0")/fixtures" 2>/dev/null && pwd)" 16cff27d90SPuranjay Mohan 17c009fabcSSong Liu# The kernel's convention: CROSS_COMPILE is the one knob, with per-tool 18c009fabcSSong Liu# overrides for what it does not cover. objtool itself is always a host binary 19c009fabcSSong Liu# -- it is built with HOSTCC and only reads ELF -- so an arm64 machine can run 20c009fabcSSong Liu# the x86 tests against x86 objects given a compiler that emits them. 21c009fabcSSong Liu# 22c009fabcSSong Liu# readelf reads any target, so it rarely needs overriding, and either GNU 23c009fabcSSong Liu# readelf or llvm-readelf will do: the assertions match on fields rather than 24c009fabcSSong Liu# on columns, and where the two spell something differently -- "OS [0xff20]" 25c009fabcSSong Liu# against "OS[0xff20]" for SHN_LIVEPATCH -- they accept both. BFD's objcopy is 26c009fabcSSong Liu# usually built for the host's target alone, and llvm-objcopy is the 27c009fabcSSong Liu# target-agnostic replacement. 28c009fabcSSong LiuCROSS_COMPILE="${CROSS_COMPILE:-}" 29c009fabcSSong LiuCC="${CC:-${CROSS_COMPILE}gcc}" 30c009fabcSSong LiuLD="${LD:-${CROSS_COMPILE}ld}" 31c009fabcSSong LiuREADELF="${READELF:-${CROSS_COMPILE}readelf}" 32c009fabcSSong LiuOBJCOPY="${OBJCOPY:-${CROSS_COMPILE}objcopy}" 33c009fabcSSong Liu 34cff27d90SPuranjay MohanOBJTOOL="${OBJTOOL:-$TESTS_DIR/../objtool}" 35c009fabcSSong Liu 36c009fabcSSong Liu# klp_preflight 37c009fabcSSong Liu# 38c009fabcSSong Liu# Check the environment once, before any test runs, and report what was found. 39c009fabcSSong Liu# 40c009fabcSSong Liuklp_preflight() 41c009fabcSSong Liu{ 42d76312b8SSong Liu local tmp tool cc_version arch host cc_arch 43c009fabcSSong Liu 44c009fabcSSong Liu bail() { echo "Bail out! $*" >&2; exit 1; } 45c009fabcSSong Liu 46c009fabcSSong Liu # A relative $OBJTOOL is relative to the objtool directory, not tests/. 47c009fabcSSong Liu [ -x "$OBJTOOL" ] || [ ! -x "$TESTS_DIR/../$OBJTOOL" ] || 48c009fabcSSong Liu OBJTOOL="$TESTS_DIR/../$OBJTOOL" 49c009fabcSSong Liu 50c009fabcSSong Liu [ -x "$OBJTOOL" ] || 51c009fabcSSong Liu bail "objtool not found at '$OBJTOOL' -- build it first" 52c009fabcSSong Liu 53c009fabcSSong Liu # run_diff() runs objtool from inside the test's working directory, so 54c009fabcSSong Liu # a relative path would resolve against that instead. 55c009fabcSSong Liu OBJTOOL="$(realpath "$OBJTOOL")" 56c009fabcSSong Liu 57c009fabcSSong Liu "$OBJTOOL" klp 2>&1 | grep -q checksum || 58c009fabcSSong Liu bail "objtool was built without klp support; install libxxhash (>= 0.8) and rebuild" 59c009fabcSSong Liu 60c009fabcSSong Liu command -v "${CC%% *}" >/dev/null || bail "compiler not found: $CC" 61c009fabcSSong Liu 62c009fabcSSong Liu for tool in "$READELF" "$OBJCOPY" "$LD"; do 63c009fabcSSong Liu command -v "${tool%% *}" >/dev/null || bail "$tool not found" 64c009fabcSSong Liu done 65c009fabcSSong Liu 66c009fabcSSong Liu tmp="$(mktemp -d)" || bail "mktemp failed" 67c009fabcSSong Liu echo 'int probe(void) { return 0; }' > "$tmp/probe.c" 68c009fabcSSong Liu $CC -c -o "$tmp/probe.o" "$tmp/probe.c" 2>/dev/null || 69c009fabcSSong Liu { rm -rf "$tmp"; bail "$CC cannot compile a trivial object"; } 70c009fabcSSong Liu 71c009fabcSSong Liu # $CC, $ARCH and objtool have to agree about the target, and cross runs 72c009fabcSSong Liu # are where they stop agreeing: plain "CC=clang ARCH=x86_64" on an arm64 73c009fabcSSong Liu # box selects the x86 tests and then builds arm64 objects, because clang 74c009fabcSSong Liu # needs --target= to emit anything but the host's. 75c009fabcSSong Liu # 76c009fabcSSong Liu # Ask objtool rather than comparing machine names. It rejects an object 77c009fabcSSong Liu # it was not built for -- "unexpected ELF machine type" -- so one check 78c009fabcSSong Liu # covers every way the three can disagree, and says so once instead of 79c009fabcSSong Liu # failing every test for the same reason. 80c009fabcSSong Liu "$OBJTOOL" klp checksum "$tmp/probe.o" >/dev/null 2>&1 || 81c009fabcSSong Liu { rm -rf "$tmp" 82c009fabcSSong Liu bail "objtool rejects an object built by '$CC'; they target" \ 83c009fabcSSong Liu "different architectures (set CROSS_COMPILE, or" \ 84c009fabcSSong Liu "--target= for clang)"; } 85c009fabcSSong Liu 86c009fabcSSong Liu # BFD objcopy is usually built for the host's target alone, and 87c009fabcSSong Liu # checksum_of() needs it to read the object under test. 88c009fabcSSong Liu $OBJCOPY -O binary --only-section=.text "$tmp/probe.o" "$tmp/probe.bin" 2>/dev/null || 89c009fabcSSong Liu { rm -rf "$tmp" 90c009fabcSSong Liu bail "$OBJCOPY cannot read objects built by '$CC'; install" \ 91c009fabcSSong Liu "binutils-multiarch or set OBJCOPY=llvm-objcopy"; } 92c009fabcSSong Liu # $ARCH only chooses which directory of tests runs, so it can disagree 93c009fabcSSong Liu # with what $CC builds without objtool noticing -- and the result is the 94c009fabcSSong Liu # wrong set of tests, quietly. 95c009fabcSSong Liu case "$($READELF -hW "$tmp/probe.o" | sed -n 's/.*Machine: *//p')" in 96c009fabcSSong Liu *X86-64*|*Intel*80386*) cc_arch=x86 ;; 97c009fabcSSong Liu *AArch64*) cc_arch=arm64 ;; 98c009fabcSSong Liu *) cc_arch= ;; 99c009fabcSSong Liu esac 100c009fabcSSong Liu rm -rf "$tmp" 101c009fabcSSong Liu 102d76312b8SSong Liu # Normalize to the kernel's SRCARCH. 103d76312b8SSong Liu case "${ARCH:-$(uname -m)}" in 104d76312b8SSong Liu x86_64|i?86) arch=x86 ;; 105d76312b8SSong Liu aarch64*) arch=arm64 ;; 106d76312b8SSong Liu *) arch="${ARCH:-$(uname -m)}" ;; 107d76312b8SSong Liu esac 108d76312b8SSong Liu 109d76312b8SSong Liu case "$(uname -m)" in 110d76312b8SSong Liu x86_64|i?86) host=x86 ;; 111d76312b8SSong Liu aarch64*) host=arm64 ;; 112d76312b8SSong Liu *) host="$(uname -m)" ;; 113d76312b8SSong Liu esac 114d76312b8SSong Liu 115d76312b8SSong Liu [ -z "$cc_arch" ] || [ "$cc_arch" = "$arch" ] || 116d76312b8SSong Liu bail "ARCH says $arch but '$CC' builds $cc_arch objects;" \ 117d76312b8SSong Liu "the $arch tests would run against the wrong architecture" 118d76312b8SSong Liu 119d76312b8SSong Liu KLP_TEST_ARCH="$arch" 120c009fabcSSong Liu KLP_TEST_PREFLIGHT=done 121d76312b8SSong Liu export OBJTOOL CC KLP_TEST_ARCH KLP_TEST_PREFLIGHT 122c009fabcSSong Liu 123c009fabcSSong Liu cc_version="$($CC --version 2>/dev/null | head -1)" 124c009fabcSSong Liu cat <<EOF 125c009fabcSSong Liu# preflight 126c009fabcSSong Liu# objtool $OBJTOOL (klp: yes) 127c009fabcSSong Liu# compiler $cc_version 128c009fabcSSong Liu# arch $KLP_TEST_ARCH$([ "$arch" = "$host" ] || echo " (host $host, cross)") 129c009fabcSSong LiuEOF 130c009fabcSSong Liu} 131c009fabcSSong Liu 132c009fabcSSong Liu[ -n "${KLP_TEST_PREFLIGHT:-}" ] || klp_preflight 133cff27d90SPuranjay Mohan 134cff27d90SPuranjay Mohan# klp-build compiles the kernel this way; klp diff needs per-symbol sections to 135cff27d90SPuranjay Mohan# extract individual functions. 136cff27d90SPuranjay MohanFIXTURE_CFLAGS="-c -O2 -ffunction-sections -fdata-sections -fno-asynchronous-unwind-tables" 137cff27d90SPuranjay Mohan 138cff27d90SPuranjay Mohantest_name="$(basename "$0" .sh)" 139cff27d90SPuranjay Mohanworkdir= 140cff27d90SPuranjay Mohan 141cff27d90SPuranjay Mohanpass() { echo "ok - $test_name${1:+: $1}"; exit 0; } 142*317cec90SSong Liufail() { echo "not ok - $test_name: $1"; exit 1; } 143*317cec90SSong Liu 144*317cec90SSong Liu# Two kinds of skip, and the runner tells them apart. 145*317cec90SSong Liu# 146*317cec90SSong Liu# declared_skip the test said in advance it does not apply here, e.g. 147*317cec90SSong Liu# gcc_only on a clang run. Expected indefinitely. 148*317cec90SSong Liu# probe_skip the construct did not turn up in the built object this 149*317cec90SSong Liu# time. Weaker: it may appear on another compiler version, 150*317cec90SSong Liu# and one which becomes permanent is a fixture that quietly 151*317cec90SSong Liu# stopped testing anything. 152*317cec90SSong Liu# 153*317cec90SSong Liu# A bare skip() is neither, and the runner counts it as a failure: a test which 154*317cec90SSong Liu# gives up for a reason it never declared is a hole, not an outcome. 155*317cec90SSong Liudeclared_skip() { echo "ok - $test_name # SKIP (declared) $*"; exit 0; } 156*317cec90SSong Liuprobe_skip() { echo "ok - $test_name # SKIP (probe) $*"; exit 0; } 157*317cec90SSong Liuskip() { echo "ok - $test_name # SKIP $*"; exit 0; } 158*317cec90SSong Liu 159*317cec90SSong Liu# TAP directives. A test which is known to fail reports it rather than being 160*317cec90SSong Liu# commented out and forgotten, and one which starts passing again says so 161*317cec90SSong Liu# instead of quietly going green: the expectation has to be removed by hand, 162*317cec90SSong Liu# which is the point. 163*317cec90SSong Liuxfail() 164*317cec90SSong Liu{ 165*317cec90SSong Liu echo "not ok - $test_name${1:+: $1} # TODO known failure" 166*317cec90SSong Liu exit 0 167*317cec90SSong Liu} 168*317cec90SSong Liu 169*317cec90SSong Liuxpass() 170*317cec90SSong Liu{ 171*317cec90SSong Liu echo "ok - $test_name${1:+: $1} # TODO expected failure, but passed" 172*317cec90SSong Liu exit 1 173*317cec90SSong Liu} 174cff27d90SPuranjay Mohan 175cff27d90SPuranjay Mohancleanup() { [ -n "$workdir" ] && rm -rf "$workdir"; } 176cff27d90SPuranjay Mohan 177cff27d90SPuranjay Mohan# setup [exported symbol...] 178cff27d90SPuranjay Mohansetup() 179cff27d90SPuranjay Mohan{ 180c009fabcSSong Liu # The environment was checked once when this file was sourced, so there 181c009fabcSSong Liu # is nothing to verify here: objtool exists at the resolved path, has 182c009fabcSSong Liu # klp support, and $CC works. 183cff27d90SPuranjay Mohan workdir="$(mktemp -d)" || fail "mktemp failed" 184cff27d90SPuranjay Mohan trap cleanup EXIT 185cff27d90SPuranjay Mohan 186cff27d90SPuranjay Mohan export_syms "$@" 187cff27d90SPuranjay Mohan} 188cff27d90SPuranjay Mohan 189cff27d90SPuranjay Mohan# export_syms [symbol...] 190cff27d90SPuranjay Mohan# 191cff27d90SPuranjay Mohan# Rewrite Module.symvers so exactly these symbols are exported by vmlinux. 192cff27d90SPuranjay Mohan# Whether a symbol is listed decides between an ordinary relocation and a klp 193cff27d90SPuranjay Mohan# relocation, so tests flip it to cover both. 194cff27d90SPuranjay Mohanexport_syms() 195cff27d90SPuranjay Mohan{ 196cff27d90SPuranjay Mohan : > "$workdir/Module.symvers" 197cff27d90SPuranjay Mohan for sym in "$@"; do 198cff27d90SPuranjay Mohan printf '0x00000000\t%s\tvmlinux\tEXPORT_SYMBOL\t\n' \ 199cff27d90SPuranjay Mohan "$sym" >> "$workdir/Module.symvers" 200cff27d90SPuranjay Mohan done 201cff27d90SPuranjay Mohan} 202cff27d90SPuranjay Mohan 203*317cec90SSong Liu# gcc_only / clang_only <reason> 204*317cec90SSong Liugcc_only() 205*317cec90SSong Liu{ 206*317cec90SSong Liu case "$($CC --version 2>/dev/null | head -1)" in 207*317cec90SSong Liu *[Gg][Cc][Cc]*) return 0 ;; 208*317cec90SSong Liu esac 209*317cec90SSong Liu declared_skip "gcc only${1:+: $1}" 210*317cec90SSong Liu} 211*317cec90SSong Liu 212*317cec90SSong Liuclang_only() 213*317cec90SSong Liu{ 214*317cec90SSong Liu case "$($CC --version 2>/dev/null | head -1)" in 215*317cec90SSong Liu *clang*) return 0 ;; 216*317cec90SSong Liu esac 217*317cec90SSong Liu declared_skip "clang only${1:+: $1}" 218*317cec90SSong Liu} 219*317cec90SSong Liu 220cff27d90SPuranjay Mohan# build_pair <fixture.c> [cflags...] 221cff27d90SPuranjay Mohanbuild_pair() 222cff27d90SPuranjay Mohan{ 223cff27d90SPuranjay Mohan local fixture="$FIXTURES_DIR/$1"; shift 224cff27d90SPuranjay Mohan 225cff27d90SPuranjay Mohan [ -f "$fixture" ] || fail "missing fixture $fixture" 226cff27d90SPuranjay Mohan 227cff27d90SPuranjay Mohan $CC $FIXTURE_CFLAGS "$@" -o "$workdir/orig.o" "$fixture" 2>"$workdir/cc.log" || 228*317cec90SSong Liu probe_skip "fixture does not build here: $(tail -1 "$workdir/cc.log")" 229cff27d90SPuranjay Mohan $CC $FIXTURE_CFLAGS "$@" -DPATCHED -o "$workdir/patched.o" "$fixture" 2>"$workdir/cc.log" || 230*317cec90SSong Liu probe_skip "fixture does not build here: $(tail -1 "$workdir/cc.log")" 231cff27d90SPuranjay Mohan} 232cff27d90SPuranjay Mohan 233cff27d90SPuranjay Mohan# run_diff [expected exit status] 234cff27d90SPuranjay Mohanrun_diff() 235cff27d90SPuranjay Mohan{ 236cff27d90SPuranjay Mohan local expect="${1:-0}" rc=0 237cff27d90SPuranjay Mohan 238cff27d90SPuranjay Mohan # Checksums live in the objects, so only generate them once even when a 239cff27d90SPuranjay Mohan # test diffs the same pair again with a different Module.symvers. 240cff27d90SPuranjay Mohan if [ ! -e "$workdir/.checksummed" ]; then 241cff27d90SPuranjay Mohan "$OBJTOOL" klp checksum "$workdir/orig.o" || 242cff27d90SPuranjay Mohan fail "klp checksum orig.o failed" 243cff27d90SPuranjay Mohan "$OBJTOOL" klp checksum "$workdir/patched.o" || 244cff27d90SPuranjay Mohan fail "klp checksum patched.o failed" 245cff27d90SPuranjay Mohan touch "$workdir/.checksummed" 246cff27d90SPuranjay Mohan fi 247cff27d90SPuranjay Mohan 248cff27d90SPuranjay Mohan # klp diff looks for Module.symvers relative to the working directory. 249cff27d90SPuranjay Mohan ( cd "$workdir" && "$OBJTOOL" klp diff orig.o patched.o out.o ) \ 250cff27d90SPuranjay Mohan > "$workdir/diff.log" 2>&1 || rc=$? 251cff27d90SPuranjay Mohan 252cff27d90SPuranjay Mohan [ "$rc" = "$expect" ] || 253cff27d90SPuranjay Mohan fail "klp diff exited $rc, expected $expect: $(tail -2 "$workdir/diff.log")" 254cff27d90SPuranjay Mohan} 255cff27d90SPuranjay Mohan 256cff27d90SPuranjay Mohancc_supports() 257cff27d90SPuranjay Mohan{ 258cff27d90SPuranjay Mohan echo 'int f(void) { return 0; }' > "$workdir/flagtest.c" 259cff27d90SPuranjay Mohan $CC $1 -c "$workdir/flagtest.c" -o "$workdir/flagtest.o" 2>/dev/null 260cff27d90SPuranjay Mohan} 261cff27d90SPuranjay Mohan 262cff27d90SPuranjay Mohan# partial_link <output> <object...> 263cff27d90SPuranjay Mohan# 264cff27d90SPuranjay Mohan# "ld -r" through the compiler driver so the link targets the same 265cff27d90SPuranjay Mohan# architecture as the objects. 266cff27d90SPuranjay Mohanpartial_link() 267cff27d90SPuranjay Mohan{ 268cff27d90SPuranjay Mohan local out="$1"; shift 269cff27d90SPuranjay Mohan 270cff27d90SPuranjay Mohan $CC -r -nostdlib -o "$out" "$@" 2>/dev/null || 271cff27d90SPuranjay Mohan $CC -r -nostdlib -fuse-ld=lld -o "$out" "$@" 2>/dev/null 272cff27d90SPuranjay Mohan} 273cff27d90SPuranjay Mohan 274cff27d90SPuranjay Mohan# find_thinlto_toolchain 275cff27d90SPuranjay Mohan# 276*317cec90SSong Liu# Set $THIN_LD to an lld from the same LLVM release as $CC (or THIN_CC). A 277cff27d90SPuranjay Mohan# mismatched pair fails with "Invalid summary version", which reads like a 278cff27d90SPuranjay Mohan# broken test rather than a broken environment. 279*317cec90SSong Liu# 280*317cec90SSong Liu# ThinLTO is clang-only; callers must use clang_only before calling this. 281*317cec90SSong Liu# Only $CC (or an explicit THIN_CC override) is consulted -- the harness does 282*317cec90SSong Liu# not search for a second compiler beside a gcc $CC. 283cff27d90SPuranjay Mohanfind_thinlto_toolchain() 284cff27d90SPuranjay Mohan{ 285*317cec90SSong Liu local cc ver ld 286cff27d90SPuranjay Mohan 287*317cec90SSong Liu for cc in "${THIN_CC:-}" "$CC"; do 288cff27d90SPuranjay Mohan [ -n "$cc" ] || continue 289*317cec90SSong Liu command -v "${cc%% *}" >/dev/null 2>&1 || return 1 290cff27d90SPuranjay Mohan 291cff27d90SPuranjay Mohan ver=$($cc -dumpversion 2>/dev/null | cut -d. -f1) 292cff27d90SPuranjay Mohan 293cff27d90SPuranjay Mohan for ld in "${THIN_LD:-}" "ld.lld-$ver" ld.lld; do 294cff27d90SPuranjay Mohan [ -n "$ld" ] || continue 295cff27d90SPuranjay Mohan command -v "$ld" >/dev/null 2>&1 || continue 296cff27d90SPuranjay Mohan 297cff27d90SPuranjay Mohan echo 'int probe(void) { return 0; }' > "$workdir/probe.c" 298cff27d90SPuranjay Mohan $cc -flto=thin -O2 -c "$workdir/probe.c" \ 299cff27d90SPuranjay Mohan -o "$workdir/probe.o" 2>/dev/null || continue 300cff27d90SPuranjay Mohan "$ld" -r "$workdir/probe.o" -o "$workdir/probe.elf" \ 301cff27d90SPuranjay Mohan 2>/dev/null || continue 302cff27d90SPuranjay Mohan 303cff27d90SPuranjay Mohan THIN_CC="$cc" 304cff27d90SPuranjay Mohan THIN_LD="$ld" 305cff27d90SPuranjay Mohan return 0 306cff27d90SPuranjay Mohan done 307cff27d90SPuranjay Mohan done 308cff27d90SPuranjay Mohan 309cff27d90SPuranjay Mohan return 1 310cff27d90SPuranjay Mohan} 311cff27d90SPuranjay Mohan 312c009fabcSSong Liuout_sections() { $READELF -S -W "$workdir/out.o" 2>/dev/null; } 313c009fabcSSong Liuout_relocs() { $READELF -r -W "$workdir/out.o" 2>/dev/null; } 314c009fabcSSong Liuout_symbols() { $READELF -s -W "$workdir/out.o" 2>/dev/null; } 315cff27d90SPuranjay Mohandiff_log() { cat "$workdir/diff.log"; } 316cff27d90SPuranjay Mohan 317cff27d90SPuranjay Mohanassert_section() 318cff27d90SPuranjay Mohan{ 319cff27d90SPuranjay Mohan out_sections | grep -q "[[:space:]]$1[[:space:]]" || 320cff27d90SPuranjay Mohan fail "expected section '$1' in output" 321cff27d90SPuranjay Mohan} 322cff27d90SPuranjay Mohan 323cff27d90SPuranjay Mohanassert_patched() 324cff27d90SPuranjay Mohan{ 325cff27d90SPuranjay Mohan assert_section ".text.$1" 326cff27d90SPuranjay Mohan} 327cff27d90SPuranjay Mohan 328cff27d90SPuranjay Mohanassert_not_patched() 329cff27d90SPuranjay Mohan{ 330cff27d90SPuranjay Mohan out_sections | grep -q "[[:space:]].text.$1[[:space:]]" && 331cff27d90SPuranjay Mohan fail "function '$1' should not have been cloned" 332cff27d90SPuranjay Mohan return 0 333cff27d90SPuranjay Mohan} 334