xref: /linux/tools/objtool/tests/generic/test-checksum-skip.sh (revision 5b6dca02b3e2254d2fd5706d76d7ab4610a67296)
1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3#
4# Symbols which must not get a checksum entry of their own.
5#
6# .discard.sym_checksum is an array of { address, checksum } looked up by the
7# address a relocation points at, so the invariant is one entry per address.
8# calculate_checksums() skips three kinds of symbol to keep it:
9#
10#   zero-length   nothing to hash, and its address belongs to whatever really
11#                 lives there
12#   alias         a second name for an address already covered
13#   cold part     hashed into its parent, which func_for_each_insn() walks
14#                 into, so its own entry would double-count
15#
16# A duplicate entry is not a build failure.  It makes the lookup ambiguous, and
17# whichever checksum loses is simply never consulted again -- so a function
18# whose code changed can be read as unchanged and dropped from the patch.
19#
20# Of the three, only the alias skip is isolated here: removing it makes this
21# test fail.  A zero-length symbol is excluded by more than one of the guards
22# at once -- its section has no data either -- so no single change makes that
23# assertion fail, and it stands as a check on the behaviour rather than on the
24# line which produces it.  Nothing here reaches the cold-part skip, which
25# wants a compiler that splits functions; test-cold-function covers that
26# symbol surviving into the patch, not its checksum.
27#
28# Covers the same ground as corpus/x86_64/checksum-zero-len-sym,
29# checksum-alias-skip and checksum-cold-skip in Joe Lawrence's klp-build unit
30# test corpus.
31
32. "$(dirname "$0")/../lib.sh"
33
34setup
35build_pair checksum_skip.c
36
37assert_input_symbol empty_marker
38assert_input_symbol alias_function
39run_checksum
40
41# entries_for <object> [-t]
42#
43# The symbol names .discard.sym_checksum has an entry for, one per line.  With
44# -t, what each entry points at instead: the name and its addend, which is the
45# address the kernel looks the entry up by.  A name alone is not that address,
46# since a relocation against a section symbol names the section and puts the
47# offset in the addend, and several entries can then share a name honestly.
48entries_for()
49{
50	in_relocs "$1" | awk -v target="${2:-}" '/rela\.discard\.sym_checksum/,/^$/ {
51		if ($1 !~ /^[0-9a-f]{8,}/)
52			next
53		if (target == "-t")
54			print $5, $6, $7
55		else
56			print $5
57	}'
58}
59
60entries="$(entries_for orig.o)"
61
62# The control: something real did get an entry, so an empty listing cannot
63# make the rest of this pass by default.
64echo "$entries" | grep -qx target ||
65	fail "no checksum entry for target"
66
67echo "$entries" | grep -qx empty_marker &&
68	fail "zero-length symbol got a checksum entry"
69
70# One of the two names for that address is kept and the other skipped; which
71# one falls out of symbol table order and is not the point.  Two would be.
72n="$(echo "$entries" | grep -cxE 'real_function|alias_function')"
73[ "$n" = 1 ] ||
74	fail "expected 1 checksum entry across real_function and its alias, found $n"
75
76# One entry per address, which is what the skipping is for.
77dupes="$(entries_for orig.o -t | sort | uniq -d)"
78[ -z "$dupes" ] ||
79	fail "two checksum entries for one address: $dupes"
80
81pass "zero-length symbols and aliases get no checksum entry of their own"
82