1*cff27d90SPuranjay Mohan# SPDX-License-Identifier: GPL-2.0 2*cff27d90SPuranjay Mohan# 3*cff27d90SPuranjay Mohan# Helpers for the objtool klp tests. A test builds a fixture twice, as the 4*cff27d90SPuranjay Mohan# original and (with -DPATCHED) the patched object, runs both through 5*cff27d90SPuranjay Mohan# "klp checksum" and diffs them, then asserts on the result. 6*cff27d90SPuranjay Mohan# 7*cff27d90SPuranjay Mohan# Assertions check properties rather than compare against recorded output: 8*cff27d90SPuranjay Mohan# codegen varies between compilers and golden files would report churn instead 9*cff27d90SPuranjay Mohan# of regressions. 10*cff27d90SPuranjay Mohan 11*cff27d90SPuranjay MohanTESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 12*cff27d90SPuranjay MohanFIXTURES_DIR="$TESTS_DIR/fixtures" 13*cff27d90SPuranjay Mohan 14*cff27d90SPuranjay MohanOBJTOOL="${OBJTOOL:-$TESTS_DIR/../objtool}" 15*cff27d90SPuranjay MohanCC="${CC:-gcc}" 16*cff27d90SPuranjay Mohan 17*cff27d90SPuranjay Mohan# klp-build compiles the kernel this way; klp diff needs per-symbol sections to 18*cff27d90SPuranjay Mohan# extract individual functions. 19*cff27d90SPuranjay MohanFIXTURE_CFLAGS="-c -O2 -ffunction-sections -fdata-sections -fno-asynchronous-unwind-tables" 20*cff27d90SPuranjay Mohan 21*cff27d90SPuranjay Mohantest_name="$(basename "$0" .sh)" 22*cff27d90SPuranjay Mohanworkdir= 23*cff27d90SPuranjay Mohan 24*cff27d90SPuranjay Mohanpass() { echo "ok - $test_name${1:+: $1}"; exit 0; } 25*cff27d90SPuranjay Mohanfail() { echo "not ok - $test_name: $1" >&2; exit 1; } 26*cff27d90SPuranjay Mohanskip() { echo "ok - $test_name # SKIP $1"; exit 0; } 27*cff27d90SPuranjay Mohan 28*cff27d90SPuranjay Mohancleanup() { [ -n "$workdir" ] && rm -rf "$workdir"; } 29*cff27d90SPuranjay Mohan 30*cff27d90SPuranjay Mohan# setup [exported symbol...] 31*cff27d90SPuranjay Mohansetup() 32*cff27d90SPuranjay Mohan{ 33*cff27d90SPuranjay Mohan # A relative $OBJTOOL is relative to the objtool directory, not to the 34*cff27d90SPuranjay Mohan # tests which run from tests/. 35*cff27d90SPuranjay Mohan [ -x "$OBJTOOL" ] || [ ! -x "$TESTS_DIR/../$OBJTOOL" ] || 36*cff27d90SPuranjay Mohan OBJTOOL="$TESTS_DIR/../$OBJTOOL" 37*cff27d90SPuranjay Mohan 38*cff27d90SPuranjay Mohan # Not finding objtool is a broken invocation, not an environment which 39*cff27d90SPuranjay Mohan # cannot run the test. Skipping here would read as a pass. 40*cff27d90SPuranjay Mohan [ -x "$OBJTOOL" ] || fail "objtool not found at '$OBJTOOL', build it first" 41*cff27d90SPuranjay Mohan 42*cff27d90SPuranjay Mohan # run_diff() runs objtool from inside the test's working directory, so 43*cff27d90SPuranjay Mohan # a relative path would resolve against that instead. 44*cff27d90SPuranjay Mohan OBJTOOL="$(realpath "$OBJTOOL")" 45*cff27d90SPuranjay Mohan 46*cff27d90SPuranjay Mohan "$OBJTOOL" klp 2>&1 | grep -q checksum || 47*cff27d90SPuranjay Mohan skip "objtool built without klp support (needs libxxhash)" 48*cff27d90SPuranjay Mohan command -v "${CC%% *}" >/dev/null || skip "no compiler ($CC)" 49*cff27d90SPuranjay Mohan 50*cff27d90SPuranjay Mohan workdir="$(mktemp -d)" || fail "mktemp failed" 51*cff27d90SPuranjay Mohan trap cleanup EXIT 52*cff27d90SPuranjay Mohan 53*cff27d90SPuranjay Mohan export_syms "$@" 54*cff27d90SPuranjay Mohan} 55*cff27d90SPuranjay Mohan 56*cff27d90SPuranjay Mohan# export_syms [symbol...] 57*cff27d90SPuranjay Mohan# 58*cff27d90SPuranjay Mohan# Rewrite Module.symvers so exactly these symbols are exported by vmlinux. 59*cff27d90SPuranjay Mohan# Whether a symbol is listed decides between an ordinary relocation and a klp 60*cff27d90SPuranjay Mohan# relocation, so tests flip it to cover both. 61*cff27d90SPuranjay Mohanexport_syms() 62*cff27d90SPuranjay Mohan{ 63*cff27d90SPuranjay Mohan : > "$workdir/Module.symvers" 64*cff27d90SPuranjay Mohan for sym in "$@"; do 65*cff27d90SPuranjay Mohan printf '0x00000000\t%s\tvmlinux\tEXPORT_SYMBOL\t\n' \ 66*cff27d90SPuranjay Mohan "$sym" >> "$workdir/Module.symvers" 67*cff27d90SPuranjay Mohan done 68*cff27d90SPuranjay Mohan} 69*cff27d90SPuranjay Mohan 70*cff27d90SPuranjay Mohan# build_pair <fixture.c> [cflags...] 71*cff27d90SPuranjay Mohanbuild_pair() 72*cff27d90SPuranjay Mohan{ 73*cff27d90SPuranjay Mohan local fixture="$FIXTURES_DIR/$1"; shift 74*cff27d90SPuranjay Mohan 75*cff27d90SPuranjay Mohan [ -f "$fixture" ] || fail "missing fixture $fixture" 76*cff27d90SPuranjay Mohan 77*cff27d90SPuranjay Mohan $CC $FIXTURE_CFLAGS "$@" -o "$workdir/orig.o" "$fixture" 2>"$workdir/cc.log" || 78*cff27d90SPuranjay Mohan skip "fixture does not build here: $(tail -1 "$workdir/cc.log")" 79*cff27d90SPuranjay Mohan $CC $FIXTURE_CFLAGS "$@" -DPATCHED -o "$workdir/patched.o" "$fixture" 2>"$workdir/cc.log" || 80*cff27d90SPuranjay Mohan skip "fixture does not build here: $(tail -1 "$workdir/cc.log")" 81*cff27d90SPuranjay Mohan} 82*cff27d90SPuranjay Mohan 83*cff27d90SPuranjay Mohan# run_diff [expected exit status] 84*cff27d90SPuranjay Mohanrun_diff() 85*cff27d90SPuranjay Mohan{ 86*cff27d90SPuranjay Mohan local expect="${1:-0}" rc=0 87*cff27d90SPuranjay Mohan 88*cff27d90SPuranjay Mohan # Checksums live in the objects, so only generate them once even when a 89*cff27d90SPuranjay Mohan # test diffs the same pair again with a different Module.symvers. 90*cff27d90SPuranjay Mohan if [ ! -e "$workdir/.checksummed" ]; then 91*cff27d90SPuranjay Mohan "$OBJTOOL" klp checksum "$workdir/orig.o" || 92*cff27d90SPuranjay Mohan fail "klp checksum orig.o failed" 93*cff27d90SPuranjay Mohan "$OBJTOOL" klp checksum "$workdir/patched.o" || 94*cff27d90SPuranjay Mohan fail "klp checksum patched.o failed" 95*cff27d90SPuranjay Mohan touch "$workdir/.checksummed" 96*cff27d90SPuranjay Mohan fi 97*cff27d90SPuranjay Mohan 98*cff27d90SPuranjay Mohan # klp diff looks for Module.symvers relative to the working directory. 99*cff27d90SPuranjay Mohan ( cd "$workdir" && "$OBJTOOL" klp diff orig.o patched.o out.o ) \ 100*cff27d90SPuranjay Mohan > "$workdir/diff.log" 2>&1 || rc=$? 101*cff27d90SPuranjay Mohan 102*cff27d90SPuranjay Mohan [ "$rc" = "$expect" ] || 103*cff27d90SPuranjay Mohan fail "klp diff exited $rc, expected $expect: $(tail -2 "$workdir/diff.log")" 104*cff27d90SPuranjay Mohan} 105*cff27d90SPuranjay Mohan 106*cff27d90SPuranjay Mohancc_supports() 107*cff27d90SPuranjay Mohan{ 108*cff27d90SPuranjay Mohan echo 'int f(void) { return 0; }' > "$workdir/flagtest.c" 109*cff27d90SPuranjay Mohan $CC $1 -c "$workdir/flagtest.c" -o "$workdir/flagtest.o" 2>/dev/null 110*cff27d90SPuranjay Mohan} 111*cff27d90SPuranjay Mohan 112*cff27d90SPuranjay Mohan# partial_link <output> <object...> 113*cff27d90SPuranjay Mohan# 114*cff27d90SPuranjay Mohan# "ld -r" through the compiler driver so the link targets the same 115*cff27d90SPuranjay Mohan# architecture as the objects. 116*cff27d90SPuranjay Mohanpartial_link() 117*cff27d90SPuranjay Mohan{ 118*cff27d90SPuranjay Mohan local out="$1"; shift 119*cff27d90SPuranjay Mohan 120*cff27d90SPuranjay Mohan $CC -r -nostdlib -o "$out" "$@" 2>/dev/null || 121*cff27d90SPuranjay Mohan $CC -r -nostdlib -fuse-ld=lld -o "$out" "$@" 2>/dev/null 122*cff27d90SPuranjay Mohan} 123*cff27d90SPuranjay Mohan 124*cff27d90SPuranjay Mohan# find_thinlto_toolchain 125*cff27d90SPuranjay Mohan# 126*cff27d90SPuranjay Mohan# Set $THIN_CC and $THIN_LD to a clang and lld from the same LLVM release. A 127*cff27d90SPuranjay Mohan# mismatched pair fails with "Invalid summary version", which reads like a 128*cff27d90SPuranjay Mohan# broken test rather than a broken environment. 129*cff27d90SPuranjay Mohanfind_thinlto_toolchain() 130*cff27d90SPuranjay Mohan{ 131*cff27d90SPuranjay Mohan local cc ld ver 132*cff27d90SPuranjay Mohan 133*cff27d90SPuranjay Mohan for cc in "${THIN_CC:-}" "$CC" clang; do 134*cff27d90SPuranjay Mohan [ -n "$cc" ] || continue 135*cff27d90SPuranjay Mohan command -v "${cc%% *}" >/dev/null 2>&1 || continue 136*cff27d90SPuranjay Mohan 137*cff27d90SPuranjay Mohan ver=$($cc -dumpversion 2>/dev/null | cut -d. -f1) 138*cff27d90SPuranjay Mohan 139*cff27d90SPuranjay Mohan for ld in "${THIN_LD:-}" "ld.lld-$ver" ld.lld; do 140*cff27d90SPuranjay Mohan [ -n "$ld" ] || continue 141*cff27d90SPuranjay Mohan command -v "$ld" >/dev/null 2>&1 || continue 142*cff27d90SPuranjay Mohan 143*cff27d90SPuranjay Mohan echo 'int probe(void) { return 0; }' > "$workdir/probe.c" 144*cff27d90SPuranjay Mohan $cc -flto=thin -O2 -c "$workdir/probe.c" \ 145*cff27d90SPuranjay Mohan -o "$workdir/probe.o" 2>/dev/null || continue 146*cff27d90SPuranjay Mohan "$ld" -r "$workdir/probe.o" -o "$workdir/probe.elf" \ 147*cff27d90SPuranjay Mohan 2>/dev/null || continue 148*cff27d90SPuranjay Mohan 149*cff27d90SPuranjay Mohan THIN_CC="$cc" 150*cff27d90SPuranjay Mohan THIN_LD="$ld" 151*cff27d90SPuranjay Mohan return 0 152*cff27d90SPuranjay Mohan done 153*cff27d90SPuranjay Mohan done 154*cff27d90SPuranjay Mohan 155*cff27d90SPuranjay Mohan return 1 156*cff27d90SPuranjay Mohan} 157*cff27d90SPuranjay Mohan 158*cff27d90SPuranjay Mohanout_sections() { readelf -S -W "$workdir/out.o" 2>/dev/null; } 159*cff27d90SPuranjay Mohanout_relocs() { readelf -r -W "$workdir/out.o" 2>/dev/null; } 160*cff27d90SPuranjay Mohanout_symbols() { readelf -s -W "$workdir/out.o" 2>/dev/null; } 161*cff27d90SPuranjay Mohandiff_log() { cat "$workdir/diff.log"; } 162*cff27d90SPuranjay Mohan 163*cff27d90SPuranjay Mohanassert_section() 164*cff27d90SPuranjay Mohan{ 165*cff27d90SPuranjay Mohan out_sections | grep -q "[[:space:]]$1[[:space:]]" || 166*cff27d90SPuranjay Mohan fail "expected section '$1' in output" 167*cff27d90SPuranjay Mohan} 168*cff27d90SPuranjay Mohan 169*cff27d90SPuranjay Mohanassert_patched() 170*cff27d90SPuranjay Mohan{ 171*cff27d90SPuranjay Mohan assert_section ".text.$1" 172*cff27d90SPuranjay Mohan} 173*cff27d90SPuranjay Mohan 174*cff27d90SPuranjay Mohanassert_not_patched() 175*cff27d90SPuranjay Mohan{ 176*cff27d90SPuranjay Mohan out_sections | grep -q "[[:space:]].text.$1[[:space:]]" && 177*cff27d90SPuranjay Mohan fail "function '$1' should not have been cloned" 178*cff27d90SPuranjay Mohan return 0 179*cff27d90SPuranjay Mohan} 180