xref: /linux/tools/objtool/tests/lib.sh (revision cff27d90769da941f6897a9d9152670af7b71f95)
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