summaryrefslogtreecommitdiff
path: root/tools/objtool/tests/generic/test-checksum-skip.sh
blob: f245535a0f363f2ebad716132847c0eebc10065d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Symbols which must not get a checksum entry of their own.
#
# .discard.sym_checksum is an array of { address, checksum } looked up by the
# address a relocation points at, so the invariant is one entry per address.
# calculate_checksums() skips three kinds of symbol to keep it:
#
#   zero-length   nothing to hash, and its address belongs to whatever really
#                 lives there
#   alias         a second name for an address already covered
#   cold part     hashed into its parent, which func_for_each_insn() walks
#                 into, so its own entry would double-count
#
# A duplicate entry is not a build failure.  It makes the lookup ambiguous, and
# whichever checksum loses is simply never consulted again -- so a function
# whose code changed can be read as unchanged and dropped from the patch.
#
# Of the three, only the alias skip is isolated here: removing it makes this
# test fail.  A zero-length symbol is excluded by more than one of the guards
# at once -- its section has no data either -- so no single change makes that
# assertion fail, and it stands as a check on the behaviour rather than on the
# line which produces it.  Nothing here reaches the cold-part skip, which
# wants a compiler that splits functions; test-cold-function covers that
# symbol surviving into the patch, not its checksum.
#
# Covers the same ground as corpus/x86_64/checksum-zero-len-sym,
# checksum-alias-skip and checksum-cold-skip in Joe Lawrence's klp-build unit
# test corpus.

. "$(dirname "$0")/../lib.sh"

setup
build_pair checksum_skip.c

assert_input_symbol empty_marker
assert_input_symbol alias_function
run_checksum

# entries_for <object> [-t]
#
# The symbol names .discard.sym_checksum has an entry for, one per line.  With
# -t, what each entry points at instead: the name and its addend, which is the
# address the kernel looks the entry up by.  A name alone is not that address,
# since a relocation against a section symbol names the section and puts the
# offset in the addend, and several entries can then share a name honestly.
entries_for()
{
	in_relocs "$1" | awk -v target="${2:-}" '/rela\.discard\.sym_checksum/,/^$/ {
		if ($1 !~ /^[0-9a-f]{8,}/)
			next
		if (target == "-t")
			print $5, $6, $7
		else
			print $5
	}'
}

entries="$(entries_for orig.o)"

# The control: something real did get an entry, so an empty listing cannot
# make the rest of this pass by default.
echo "$entries" | grep -qx target ||
	fail "no checksum entry for target"

echo "$entries" | grep -qx empty_marker &&
	fail "zero-length symbol got a checksum entry"

# One of the two names for that address is kept and the other skipped; which
# one falls out of symbol table order and is not the point.  Two would be.
n="$(echo "$entries" | grep -cxE 'real_function|alias_function')"
[ "$n" = 1 ] ||
	fail "expected 1 checksum entry across real_function and its alias, found $n"

# One entry per address, which is what the skipping is for.
dupes="$(entries_for orig.o -t | sort | uniq -d)"
[ -z "$dupes" ] ||
	fail "two checksum entries for one address: $dupes"

pass "zero-length symbols and aliases get no checksum entry of their own"