1.. SPDX-License-Identifier: GPL-2.0 2 3====================================== 4Design of the objtool klp test harness 5====================================== 6 7tools/objtool/tests/ holds unit tests for the klp subcommands of 8objtool -- ``klp checksum``, ``klp diff``, ``klp post-link`` and 9``--klp-symids`` -- which together turn two builds of the kernel into a 10livepatch module. 11 12This document explains how the harness is built and why. For the rules to 13follow when adding a test, see klp-write-tests.txt. 14 15 16TL;DR 17===== 18 19One run covers one compiler and one architecture; CI runs the combinations. 20Build objtool first -- it needs libelf and libxxhash -- and the same ARCH is 21used for both steps. 22 23Natively, with gcc:: 24 25 make -C tools/objtool 26 make -C tools/objtool tests 27 28Natively, with clang -- LLVM=1 additionally selects the LLVM binutils:: 29 30 CC=clang make -C tools/objtool tests 31 LLVM=1 make -C tools/objtool tests 32 33Cross, with gcc -- an arm64 host running the x86 tests:: 34 35 ARCH=x86_64 CROSS_COMPILE=x86_64-linux-gnu- make -C tools/objtool 36 ARCH=x86_64 CROSS_COMPILE=x86_64-linux-gnu- make -C tools/objtool tests 37 38Cross, with clang. It defaults to the host triple however it is invoked, so 39--target= is what makes it emit x86; OBJCOPY is needed because BFD's is 40usually built for the host's target alone:: 41 42 ARCH=x86_64 make -C tools/objtool 43 ARCH=x86_64 CC="clang --target=x86_64-linux-gnu" OBJCOPY=llvm-objcopy \ 44 make -C tools/objtool tests 45 46A run ends with a totals line; anything other than fail:0 is a real result:: 47 48 # pass:48 fail:0 static-skip:1 probe-skip:0 xfail:0 xpass:0 49 50Useful extras:: 51 52 tools/objtool/tests/run-tests.sh basic # one test, by name 53 tools/objtool/tests/run-tests.sh --keep basic # and keep what it built 54 55 56Why unit tests are possible at all 57================================== 58 59klp-build is a pipeline: build the kernel twice, checksum both, diff them, 60link the result. Testing that end to end means two kernel builds per case, 61which is too slow to run often and too heavy to keep in the tree. 62 63Three properties make a much cheaper test possible. 64 65**objtool has no configuration-dependent logic.** It never reads ``.config``. 66Every ``CONFIG_`` string in its source is a comment or one error message, and 67its only build-time conditionals are driven by host libraries and the target 68architecture. Configuration reaches objtool through exactly two channels: the 69``objtool-args-$(CONFIG_*)`` lines in scripts/Makefile.lib, and the 70contents of the object handed to it. 71 72**The klp subcommands use none of the first channel.** Of objtool's options 73they consult three -- ``checksum``, ``debug_checksum``, ``dryrun`` -- all from 74their own command line. So klp behaviour varies with configuration *only* 75through the input object. 76 77**Therefore a test can reproduce any configuration's behaviour by reproducing 78its input.** Compile a small freestanding fixture with the flags that 79configuration would have used, and objtool cannot tell the difference. No 80kernel, no ``.config``, no object cache. 81 82The whole suite runs in a few seconds. 83 84 85Shape of a test 86=============== 87 88Each test compiles one fixture twice -- once plain, once with ``-DPATCHED`` -- 89runs ``klp checksum`` over both, diffs them, and asserts on properties of the 90output object:: 91 92 . "$(dirname "$0")/../lib.sh" 93 94 setup 95 build_pair basic.c 96 97 assert_input_symbol changed 98 run_diff 99 100 assert_patched changed 101 assert_not_patched untouched 102 103 pass "changed function cloned, unchanged function left alone" 104 105Assertions check properties, never recorded output. Codegen varies between 106compilers and versions, so a golden file would report churn rather than 107regressions. 108 109 110Layout 111====== 112 113:: 114 115 tools/objtool/tests/ 116 lib.sh the harness: everything a test may call 117 run-tests.sh selects, runs and classifies 118 generic/ 119 test-*.sh 120 fixtures/*.c 121 x86/ 122 test-*.sh 123 fixtures/*.c 124 125Which architecture a test is for is expressed by where it lives. The runner 126executes ``generic/`` plus the directory matching this architecture, so a test 127which cannot apply is not run rather than running in order to report that it 128did not. There is no ``x86_only`` helper, and no lookup letting an 129architecture fixture shadow a generic one: an architecture-specific test 130carries its own fixtures. 131 132Compilers cannot be expressed the same way, because CI varies ``CC`` over the 133same tree. A compiler requirement stays a declaration inside the test 134(``gcc_only``, ``clang_only``). 135 136 137The environment is established once 138=================================== 139 140Sourcing lib.sh runs ``klp_preflight``, which checks that objtool 141exists and has klp support, that ``$CC`` works, that the binutils are present, 142and which architecture this is. The answers are exported, so: 143 144* ``run-tests.sh`` sources lib.sh too, and therefore knows the 145 architecture before it chooses which tests to run; 146* each test inherits the answers rather than repeating the work; 147* a test run on its own establishes them for itself. 148 149Preflight answers only whether the suite can run at all. A suite which cannot 150run must not exit 0 looking like one which passed, so a missing objtool fails 151the whole run with a TAP ``Bail out!`` rather than skipping each test in turn. 152What a *particular* compiler can do is a different question, left to the test 153which cares. 154 155 156Outcomes 157======== 158 159Output is TAP. The distinction the harness cares most about is between kinds 160of skip, because a skip is how a suite quietly stops testing anything: 161 162``declared`` 163 The test said in advance it does not apply -- ``gcc_only`` on a clang run. 164 Expected indefinitely. 165 166``probe`` 167 The construct did not turn up in the built object this time. Weaker: one 168 which becomes permanent is a fixture that has stopped testing anything. 169 170``undeclared`` 171 Counted as a **failure**. A test which gives up for a reason it never 172 declared is a hole, not an outcome. 173 174``xfail``/``xpass`` come with them, so a known failure is reported rather than 175commented out, and one which starts passing says so instead of going quietly 176green. 177 178The runner classifies the TAP result line, not everything a test printed: 179objtool warns on stderr and that output is captured, so a stray line ahead of 180the result would otherwise leave the exit status to decide -- and an expected 181failure exits 0. 182 183A run ends with a totals line:: 184 185 # pass:48 fail:0 static-skip:1 probe-skip:0 xfail:0 xpass:0 186 187and reports what it left out:: 188 189 # not run: 5 tests in x86/ (this run is arm64) 190 191 192Working directories 193=================== 194 195A run gets one directory; each test gets a subdirectory of it, mirroring the 196source layout:: 197 198 /tmp/klp-tests.XXXXXXXX/ 199 generic/test-basic/{orig.o,patched.o,out.o,Module.symvers,...} 200 x86/test-kcfi/... 201 202By default (``KEEP=failed``) only failing tests keep their directories; the 203runner reports where they are. ``KEEP=all`` keeps every test's directory; 204``KEEP=none`` removes them all. The runner ``rmdir``s the run directory when 205it is empty -- which fails if anything was left behind unexpectedly, so a test 206which dies without cleaning up is reported rather than silently leaking. 207 208 209Running 210======= 211 212:: 213 214 make -C tools/objtool # needs libelf and libxxhash 215 make -C tools/objtool tests 216 217 CC=clang make -C tools/objtool tests # the other toolchain 218 LLVM=1 make -C tools/objtool tests # and its binutils too 219 220 make -C tools/objtool tests KEEP=all # keep every test's workdir 221 make -C tools/objtool tests KEEP=none # remove all workdirs 222 223 tools/objtool/tests/run-tests.sh basic # one test; failures kept by default 224 225A run covers one compiler and one architecture; CI runs the combinations. 226 227Cross-compiled runs 228------------------- 229 230objtool klp is built only where ARCH_HAS_KLP is set, which today means x86 -- 231so an arm64 machine cannot run any of this natively. It can run all of it 232cross, because objtool is a host tool that only reads and rewrites ELF, and 233the tests only compile fixtures and inspect the objects. Nothing has to 234execute target code. 235 236:: 237 238 ARCH=x86_64 CROSS_COMPILE=x86_64-linux-gnu- make -C tools/objtool 239 ARCH=x86_64 CROSS_COMPILE=x86_64-linux-gnu- make -C tools/objtool tests 240 241objtool itself stays a native binary: it is built with HOSTCC, not CC, so 242setting a cross compiler cannot produce one the host is unable to run. ARCH 243selects both the objtool target and the directory of tests to run. 244 245clang needs telling, since it defaults to the host triple however it is 246invoked. LLVM=1 with CROSS_COMPILE does that for you -- the --target= it 247derives is forwarded to the tests -- and the fixtures include no kernel 248headers, so no sysroot is needed: 249 250:: 251 252 ARCH=x86_64 CROSS_COMPILE=x86_64-linux-gnu- LLVM=1 \ 253 make -C tools/objtool tests 254 255Naming the compiler by hand works too, and is what to reach for when the 256target triple is not the one CROSS_COMPILE implies: 257 258:: 259 260 ARCH=x86_64 CC="clang --target=x86_64-linux-gnu" \ 261 OBJCOPY=llvm-objcopy make -C tools/objtool tests 262 263CROSS_COMPILE picks the binutils, and each can be overridden on its own. 264readelf reads any target and rarely needs overriding; BFD's objcopy is usually 265built for the host's alone, hence OBJCOPY=llvm-objcopy above, or install 266binutils-multiarch. 267 268Either readelf will do. The assertions read readelf's output, and the two 269spell some of it differently -- GNU prints "OS [0xff20]" for SHN_LIVEPATCH 270where llvm-readelf prints "OS[0xff20]" -- so they accept both. 271 272Getting this wrong is easy and the harness refuses rather than producing a 273misleading result. "CC=clang ARCH=x86_64" alone selects the x86 tests and 274then builds arm64 objects; preflight compiles a probe object, hands it to 275objtool, and stops the run if they disagree about the architecture, or if 276ARCH does not match what the compiler emits. 277 278 279What this does not cover 280======================== 281 282These are unit tests for objtool's klp subcommands. They do not build a 283kernel, do not run scripts/livepatch/klp-build, and do not load a 284livepatch. Behaviour which only appears when the kernel applies a patch -- 285the module loader refusing a relocation, late module patching ordering -- has 286to be tested by booting, and is out of scope here. 287