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 11TESTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 12 13# Tests live in generic/ or in an architecture directory beside it, and each 14# carries its own fixtures. 15FIXTURES_DIR="$(cd "$(dirname "$0")/fixtures" 2>/dev/null && pwd)" 16 17# The kernel's convention: CROSS_COMPILE is the one knob, with per-tool 18# overrides for what it does not cover. objtool itself is always a host binary 19# -- it is built with HOSTCC and only reads ELF -- so an arm64 machine can run 20# the x86 tests against x86 objects given a compiler that emits them. 21# 22# readelf reads any target, so it rarely needs overriding, and either GNU 23# readelf or llvm-readelf will do: the assertions match on fields rather than 24# on columns, and where the two spell something differently -- "OS [0xff20]" 25# against "OS[0xff20]" for SHN_LIVEPATCH -- they accept both. BFD's objcopy is 26# usually built for the host's target alone, and llvm-objcopy is the 27# target-agnostic replacement. 28CROSS_COMPILE="${CROSS_COMPILE:-}" 29CC="${CC:-${CROSS_COMPILE}gcc}" 30LD="${LD:-${CROSS_COMPILE}ld}" 31READELF="${READELF:-${CROSS_COMPILE}readelf}" 32OBJCOPY="${OBJCOPY:-${CROSS_COMPILE}objcopy}" 33 34OBJTOOL="${OBJTOOL:-$TESTS_DIR/../objtool}" 35 36# klp_preflight 37# 38# Check the environment once, before any test runs, and report what was found. 39# 40klp_preflight() 41{ 42 local tmp tool cc_version arch host cc_arch 43 44 bail() { echo "Bail out! $*" >&2; exit 1; } 45 46 # A relative $OBJTOOL is relative to the objtool directory, not tests/. 47 [ -x "$OBJTOOL" ] || [ ! -x "$TESTS_DIR/../$OBJTOOL" ] || 48 OBJTOOL="$TESTS_DIR/../$OBJTOOL" 49 50 [ -x "$OBJTOOL" ] || 51 bail "objtool not found at '$OBJTOOL' -- build it first" 52 53 # run_diff() runs objtool from inside the test's working directory, so 54 # a relative path would resolve against that instead. 55 OBJTOOL="$(realpath "$OBJTOOL")" 56 57 "$OBJTOOL" klp 2>&1 | grep -q checksum || 58 bail "objtool was built without klp support; install libxxhash (>= 0.8) and rebuild" 59 60 command -v "${CC%% *}" >/dev/null || bail "compiler not found: $CC" 61 62 for tool in "$READELF" "$OBJCOPY" "$LD"; do 63 command -v "${tool%% *}" >/dev/null || bail "$tool not found" 64 done 65 66 tmp="$(mktemp -d)" || bail "mktemp failed" 67 echo 'int probe(void) { return 0; }' > "$tmp/probe.c" 68 $CC -c -o "$tmp/probe.o" "$tmp/probe.c" 2>/dev/null || 69 { rm -rf "$tmp"; bail "$CC cannot compile a trivial object"; } 70 71 # $CC, $ARCH and objtool have to agree about the target, and cross runs 72 # are where they stop agreeing: plain "CC=clang ARCH=x86_64" on an arm64 73 # box selects the x86 tests and then builds arm64 objects, because clang 74 # needs --target= to emit anything but the host's. 75 # 76 # Ask objtool rather than comparing machine names. It rejects an object 77 # it was not built for -- "unexpected ELF machine type" -- so one check 78 # covers every way the three can disagree, and says so once instead of 79 # failing every test for the same reason. 80 "$OBJTOOL" klp checksum "$tmp/probe.o" >/dev/null 2>&1 || 81 { rm -rf "$tmp" 82 bail "objtool rejects an object built by '$CC'; they target" \ 83 "different architectures (set CROSS_COMPILE, or" \ 84 "--target= for clang)"; } 85 86 # BFD objcopy is usually built for the host's target alone, and 87 # checksum_of() needs it to read the object under test. 88 $OBJCOPY -O binary --only-section=.text "$tmp/probe.o" "$tmp/probe.bin" 2>/dev/null || 89 { rm -rf "$tmp" 90 bail "$OBJCOPY cannot read objects built by '$CC'; install" \ 91 "binutils-multiarch or set OBJCOPY=llvm-objcopy"; } 92 # $ARCH only chooses which directory of tests runs, so it can disagree 93 # with what $CC builds without objtool noticing -- and the result is the 94 # wrong set of tests, quietly. 95 case "$($READELF -hW "$tmp/probe.o" | sed -n 's/.*Machine: *//p')" in 96 *X86-64*|*Intel*80386*) cc_arch=x86 ;; 97 *AArch64*) cc_arch=arm64 ;; 98 *) cc_arch= ;; 99 esac 100 rm -rf "$tmp" 101 102 # Normalize to the kernel's SRCARCH. 103 case "${ARCH:-$(uname -m)}" in 104 x86_64|i?86) arch=x86 ;; 105 aarch64*) arch=arm64 ;; 106 *) arch="${ARCH:-$(uname -m)}" ;; 107 esac 108 109 case "$(uname -m)" in 110 x86_64|i?86) host=x86 ;; 111 aarch64*) host=arm64 ;; 112 *) host="$(uname -m)" ;; 113 esac 114 115 [ -z "$cc_arch" ] || [ "$cc_arch" = "$arch" ] || 116 bail "ARCH says $arch but '$CC' builds $cc_arch objects;" \ 117 "the $arch tests would run against the wrong architecture" 118 119 KLP_TEST_ARCH="$arch" 120 KLP_TEST_PREFLIGHT=done 121 export OBJTOOL CC KLP_TEST_ARCH KLP_TEST_PREFLIGHT 122 123 cc_version="$($CC --version 2>/dev/null | head -1)" 124 cat <<EOF 125# preflight 126# objtool $OBJTOOL (klp: yes) 127# compiler $cc_version 128# arch $KLP_TEST_ARCH$([ "$arch" = "$host" ] || echo " (host $host, cross)") 129EOF 130} 131 132[ -n "${KLP_TEST_PREFLIGHT:-}" ] || klp_preflight 133 134# What every fixture is built with. These describe the kernel a fixture stands 135# in for; -c is build_one's contract rather than a property of that kernel, so 136# it lives at the compile where an override cannot drop it. 137# 138# -O2 the kernel's default 139# -ffunction-sections -fdata-sections klp-build passes these itself, through 140# KCFLAGS, whatever the configuration 141# -fno-asynchronous-unwind-tables arch/x86/Makefile sets this always, so 142# kernel objects carry no .eh_frame 143# -fno-common the kernel's Makefile sets it, so an 144# uninitialised global there lands in 145# .bss rather than being SHN_COMMON, 146# which has no section and so no 147# checksum 148# 149# A test overrides it; see tools/objtool/Documentation/klp-write-tests.txt. 150FIXTURE_CFLAGS="-O2 -ffunction-sections -fdata-sections -fno-common \ 151 -fno-asynchronous-unwind-tables" 152 153test_name="$(basename "$0" .sh)" 154workdir= 155 156# The pair run_diff() and the checksum helpers work on. build_pair() names 157# them again and make_vmlinux_pair() repoints orig_obj at the image it links, 158# but a test which builds its objects itself with build_one() sets neither, so 159# the default belongs here. 160orig_obj=orig.o 161patched_obj=patched.o 162 163pass() { echo "ok - $test_name${1:+: $1}"; exit 0; } 164fail() { echo "not ok - $test_name: $1"; exit 1; } 165 166# Two kinds of skip, and the runner tells them apart. 167# 168# declared_skip the test said in advance it does not apply here, e.g. 169# gcc_only on a clang run. Expected indefinitely. 170# probe_skip the construct did not turn up in the built object this 171# time. Weaker: it may appear on another compiler version, 172# and one which becomes permanent is a fixture that quietly 173# stopped testing anything. 174# 175# A bare skip() is neither, and the runner counts it as a failure: a test which 176# gives up for a reason it never declared is a hole, not an outcome. 177declared_skip() { echo "ok - $test_name # SKIP (declared) $*"; exit 0; } 178probe_skip() { echo "ok - $test_name # SKIP (probe) $*"; exit 0; } 179skip() { echo "ok - $test_name # SKIP $*"; exit 0; } 180 181# TAP directives. A test which is known to fail reports it rather than being 182# commented out and forgotten, and one which starts passing again says so 183# instead of quietly going green: the expectation has to be removed by hand, 184# which is the point. 185xfail() 186{ 187 echo "not ok - $test_name${1:+: $1} # TODO known failure" 188 exit 0 189} 190 191xpass() 192{ 193 echo "ok - $test_name${1:+: $1} # TODO expected failure, but passed" 194 exit 1 195} 196 197cleanup() { [ -n "$workdir" ] && rm -rf "$workdir"; } 198 199# setup [exported symbol...] 200setup() 201{ 202 # The environment was checked once when this file was sourced, so there 203 # is nothing to verify here: objtool exists at the resolved path, has 204 # klp support, and $CC works. 205 workdir="$(mktemp -d)" || fail "mktemp failed" 206 trap cleanup EXIT 207 208 export_syms "$@" 209} 210 211# export_syms [symbol...] 212# 213# Rewrite Module.symvers so exactly these symbols are exported by vmlinux. 214# Whether a symbol is listed decides between an ordinary relocation and a klp 215# relocation, so tests flip it to cover both. 216export_syms() 217{ 218 : > "$workdir/Module.symvers" 219 for sym in "$@"; do 220 printf '0x00000000\t%s\tvmlinux\tEXPORT_SYMBOL\t\n' \ 221 "$sym" >> "$workdir/Module.symvers" 222 done 223} 224 225# gcc_only / clang_only <reason> 226gcc_only() 227{ 228 case "$($CC --version 2>/dev/null | head -1)" in 229 *[Gg][Cc][Cc]*) return 0 ;; 230 esac 231 declared_skip "gcc only${1:+: $1}" 232} 233 234clang_only() 235{ 236 case "$($CC --version 2>/dev/null | head -1)" in 237 *clang*) return 0 ;; 238 esac 239 declared_skip "clang only${1:+: $1}" 240} 241 242# build_one <fixture.c> <output object> [cflags...] 243build_one() 244{ 245 local fixture out 246 fixture="$FIXTURES_DIR/$1" 247 out="$workdir/$2" 248 shift 2 249 250 [ -f "$fixture" ] || fail "missing fixture $fixture" 251 252 # run_checksum only runs once per workdir. A fresh object has no 253 # checksums in it, so anything built now needs that to happen again. 254 rm -f "$workdir/.checksummed" 255 256 $CC -c $FIXTURE_CFLAGS "$@" -o "$out" "$fixture" 2>"$workdir/cc.log" || 257 fail "$(basename "$fixture") does not build: $(tail -1 "$workdir/cc.log")" 258} 259 260# build_pair <fixture.c> [cflags...] 261build_pair() 262{ 263 local fixture="$1"; shift 264 265 # Name what this builds. A test may run several segments, and 266 # make_vmlinux_pair() repoints orig_obj at the image it links, so 267 # without this the next run_diff() would still be reading that. 268 orig_obj=orig.o 269 patched_obj=patched.o 270 271 build_one "$fixture" orig.o "$@" 272 build_one "$fixture" patched.o "$@" -DPATCHED 273} 274 275# run_objtool_check <objtool arguments...> 276# 277# Run objtool's ordinary check pass over the pair, as the kernel build does. 278# 279# Some of what klp diff consumes is produced by this pass rather than by the 280# compiler: .static_call_sites, .mcount_loc, .ibt_endbr_seal, ORC. 281# 282# Only module objects see it before klp-build -- with CONFIG_KLP_BUILD the 283# per-object pass is deferred, so built-in objects reach klp diff exactly as 284# the compiler left them. 285run_objtool_check() 286{ 287 local obj 288 289 # This rewrites both objects, so checksums taken before it describe 290 # something that no longer exists. As in build_one(), drop the marker 291 # so run_checksum() takes them again. 292 rm -f "$workdir/.checksummed" 293 294 for obj in "$orig_obj" "$patched_obj"; do 295 "$OBJTOOL" "$@" "$workdir/$obj" || 296 fail "objtool $* failed on $obj" 297 done 298} 299 300run_checksum() 301{ 302 # Checksums live in the objects, and a test may ask for them more than 303 # once -- diffing the same pair again with a different Module.symvers, 304 # say. objtool does the right thing when asked twice, leaving the 305 # object alone, but it says so, and that warning would be most of what 306 # a passing run prints. Remember instead, and keep quiet. 307 [ -e "$workdir/.checksummed" ] && return 0 308 309 "$OBJTOOL" klp checksum "$workdir/$orig_obj" || 310 fail "klp checksum $orig_obj failed" 311 "$OBJTOOL" klp checksum "$workdir/$patched_obj" || 312 fail "klp checksum $patched_obj failed" 313 touch "$workdir/.checksummed" 314} 315 316# run_diff [expected exit status] 317run_diff() 318{ 319 local expect="${1:-0}" rc=0 320 321 run_checksum 322 323 # klp diff looks for Module.symvers relative to the working directory. 324 ( cd "$workdir" && "$OBJTOOL" klp diff "$orig_obj" "$patched_obj" out.o ) \ 325 > "$workdir/diff.log" 2>&1 || rc=$? 326 327 [ "$rc" = "$expect" ] || 328 fail "klp diff exited $rc, expected $expect: $(tail -2 "$workdir/diff.log")" 329} 330 331cc_supports() 332{ 333 echo 'int f(void) { return 0; }' > "$workdir/flagtest.c" 334 $CC $1 -c "$workdir/flagtest.c" -o "$workdir/flagtest.o" 2>/dev/null 335} 336 337# partial_link <output> <object...> 338# 339# "ld -r" through the compiler driver so the link targets the same 340# architecture as the objects. 341partial_link() 342{ 343 local out="$1"; shift 344 345 rm -f "$workdir/.checksummed" 346 347 $CC -r -nostdlib -o "$out" "$@" 2>/dev/null || 348 $CC -r -nostdlib -fuse-ld=lld -o "$out" "$@" 2>/dev/null 349} 350 351# link_vmlinux <output> <object...> 352# 353# Link objects into an executable, the way the kernel's final link produces 354# vmlinux from vmlinux.o. Entry point 0 and no libc: nothing runs it, it only 355# has to be a linked image with resolved addresses. 356# 357# The sub-sections have to come out in name order rather than object order, 358# the way the kernel's linker script gathers .text.unlikely and .data.. apart 359# from the rest. That reordering is the entire reason .klp.symid exists: a 360# link which preserves order cannot tell a correct sympos from one that merely 361# counted, and the caller checks the two orders really did diverge. 362# 363# A linker script rather than --sort-section=name, because lld accepts that 364# option and ignores it -- so on a host where only lld can link the target, the 365# test would quietly stop testing the thing it is named for. 366# 367# Three attempts because a cross run has neither $LD nor the compiler's default 368# linker able to touch the target: on an arm64 host linking x86 objects, only 369# lld will do it. 370link_vmlinux() 371{ 372 local out="$1" lds="$workdir/sort.lds"; shift 373 374 echo 'SECTIONS { .data : { *(SORT_BY_NAME(.data.*)) } }' > "$lds" 375 376 $LD -e 0 -T "$lds" -o "$out" "$@" 2>/dev/null || 377 $CC -nostdlib -Wl,-e,0 -Wl,-T,"$lds" \ 378 -o "$out" "$@" 2>/dev/null || 379 $CC -nostdlib -fuse-ld=lld -Wl,-e,0 -Wl,-T,"$lds" \ 380 -o "$out" "$@" 2>/dev/null 381} 382 383# make_vmlinux_pair <orig object...> -- <patched object...> 384# 385# Build the vmlinux.o / vmlinux pair klp diff needs to resolve sympos the way 386# it does for built-in code, and point the diff at it. 387# 388# For a module, sympos is a count in symbol table order, which klp diff can do 389# from the object alone. vmlinux is different: the final link reorders 390# sub-sections, so the position comes from the linked image, bridged by 391# .klp.symid. klp diff only looks for that when the object it was handed is 392# called vmlinux.o and a vmlinux sits beside it -- so both the name and the 393# linked image matter. 394make_vmlinux_pair() 395{ 396 local orig=() patched=() seen= arg 397 398 for arg in "$@"; do 399 if [ "$arg" = -- ]; then seen=y; continue; fi 400 if [ -n "$seen" ]; then patched+=( "$arg" ); else orig+=( "$arg" ); fi 401 done 402 403 # Both sides have to have been named. Without this, forgetting the -- 404 # leaves one list empty, the link of nothing fails, and the test skips 405 # saying the toolchain cannot link -- which is a test bug wearing the 406 # costume of an environment one. 407 [ "${#orig[@]}" -gt 0 ] && [ "${#patched[@]}" -gt 0 ] || 408 fail "make_vmlinux_pair needs objects either side of --" 409 410 partial_link "$workdir/vmlinux.o" "${orig[@]}" || 411 probe_skip "partial link unavailable" 412 partial_link "$workdir/patched.o" "${patched[@]}" || 413 probe_skip "partial link unavailable" 414 415 "$OBJTOOL" --klp-symids --link "$workdir/vmlinux.o" || 416 fail "objtool --klp-symids failed" 417 418 link_vmlinux "$workdir/vmlinux" "$workdir/vmlinux.o" || 419 probe_skip "cannot link a vmlinux here" 420 421 orig_obj=vmlinux.o 422} 423 424# build_module_pair <fixture.c> <module name> [cflags...] 425# 426# Build the pair as objects belonging to a module rather than to vmlinux. klp 427# diff reads the object's module name from .modinfo, and that decides which 428# object a relocation is attributed to and whether a reference counts as 429# cross-module, so a good deal of the code has a module path the vmlinux 430# fixtures never reach. 431# 432# The fixture defines its .modinfo name from MODNAME. Passing that through 433# -D needs two levels of quoting, which is easy to get wrong at the call site. 434build_module_pair() 435{ 436 local fixture="$1" modname="$2"; shift 2 437 438 build_pair "$fixture" -DMODNAME="\"$modname\"" "$@" 439} 440 441# find_thinlto_toolchain 442# 443# Set $THIN_LD to an lld from the same LLVM release as $CC (or THIN_CC). A 444# mismatched pair fails with "Invalid summary version", which reads like a 445# broken test rather than a broken environment. 446# 447# ThinLTO is clang-only; callers must use clang_only before calling this. 448# Only $CC (or an explicit THIN_CC override) is consulted -- the harness does 449# not search for a second compiler beside a gcc $CC. 450find_thinlto_toolchain() 451{ 452 local cc ver ld 453 454 for cc in "${THIN_CC:-}" "$CC"; do 455 [ -n "$cc" ] || continue 456 command -v "${cc%% *}" >/dev/null 2>&1 || return 1 457 458 ver=$($cc -dumpversion 2>/dev/null | cut -d. -f1) 459 460 for ld in "${THIN_LD:-}" "ld.lld-$ver" ld.lld; do 461 [ -n "$ld" ] || continue 462 command -v "$ld" >/dev/null 2>&1 || continue 463 464 echo 'int probe(void) { return 0; }' > "$workdir/probe.c" 465 $cc -flto=thin -O2 -c "$workdir/probe.c" \ 466 -o "$workdir/probe.o" 2>/dev/null || continue 467 "$ld" -r "$workdir/probe.o" -o "$workdir/probe.elf" \ 468 2>/dev/null || continue 469 470 THIN_CC="$cc" 471 THIN_LD="$ld" 472 return 0 473 done 474 done 475 476 return 1 477} 478 479out_sections() { $READELF -S -W "$workdir/out.o" 2>/dev/null; } 480out_relocs() { $READELF -r -W "$workdir/out.o" 2>/dev/null; } 481out_symbols() { $READELF -s -W "$workdir/out.o" 2>/dev/null; } 482diff_log() { cat "$workdir/diff.log"; } 483 484assert_section() 485{ 486 out_sections | grep -q "[[:space:]]$1[[:space:]]" || 487 fail "expected section '$1' in output" 488} 489 490assert_patched() 491{ 492 assert_section ".text.$1" 493} 494 495assert_not_patched() 496{ 497 out_sections | grep -q "[[:space:]].text.$1[[:space:]]" && 498 fail "function '$1' should not have been cloned" 499 return 0 500} 501