Home Home > GIT Browse
summaryrefslogtreecommitdiff
blob: 45d044ba67c7c1f90c3c640b7a203a9242a93c55 (plain)
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/bin/bash
# testmodule
# 
# Usage: testmodule versiondumpfile module.ko [module.ko [...]]
# 
# Reads the undefined symbols and the versions of the imported
# symbols from module.ko and compares them to the list of
# symbol versions found in versiondumpfile.
# versiondumpfile should be produced by collect_ksyms
# or be a symvers file from the SUSE kernel build process (in /boot)
# or be a kernel RPM
#
# (c) Kurt Garloff <garloff@suse.de>, 2005-05-17
# License: GNU GPL

unset LANG
export LC_ALL=POSIX
export PATH=$PATH:/sbin

# Arguments: list of symbols to match
filter()
{
	awk '
		BEGIN   { invert = 0
		  for (i=1; i<ARGC; i++) {
		    sym[ARGV[i]] = 1;
		  }
		  split("", ARGV)
		}
		($1 in sym) { print }
	' "$@"
}
# Arguments: list of symbols to match
filter_out()
{
	awk '
		BEGIN   { invert = 0
		  for (i=1; i<ARGC; i++) {
		    sym[ARGV[i]] = 1;
		  }
		  split("", ARGV)
		}
		!($1 in sym) { print }
	' "$@"
}

# Arg filename
parse_symvers()
{
	if test ! -r $1; then
		echo "$1 not accessible"
		exit 6
	fi
	case $1 in 
		*.gz)
			provided=$(zcat $1 | grep '^0x' | sed 's/^\(0x[^ 	]*\)[ 	]\([^ 	]*\)/\2\t\1/')
			;;
		*)
			provided=$(grep '^0x' $1 | sed 's/^\(0x[^ 	]*\)[ 	]\([^ 	]*\)/\2\t\1/')
			;;
	esac
}
	
# Arg kernel RPM
parse_symvers_from_rpm()
{
	if test ! -r $1; then
		echo "$1 not accessible"
		exit 7
	fi
	name=$1
	DIR=$PWD
	VERSION=${name##*kernel-}
	# SAFE tmp dir creation!
	PREFIX=$(mktemp -d /tmp/kernel-$VERSION.XXXXXX)
	cd $PREFIX
	if test "${name#/}" = "${name}"; then
		unrpm $DIR/$1 >/dev/null 2>&1
	else
		unrpm $1 >/dev/null 2>&1
	fi
	VERSION=${VERSION%.*}
	VERSION=$(echo $VERSION | sed 's/^\([^-]*\)-\(.*\)\.\(.*\)$/\2-\3-\1/')
	if test -r $PREFIX/boot/modversions-$VERSION.gz; then 
		parse_symvers $PREFIX/boot/modversions-$VERSION.gz
	else
		parse_symvers $PREFIX/boot/symvers-$VERSION.gz
	fi
	cd $DIR
	rm -rf $PREFIX
}

# Argument: filename
add_exported_syms()
{
	if test ! -r $1; then
		echo "$1 not accessible"
		exit 8
	fi
	name=$1; name=${name%.ko}; name=${name##*modules/}; name=${name#*/}
	# Append newly found symbols
	crc=$(nm $1 | grep __crc_ | sed "s@^\(00000000\|\)\([0-9a-f]*\) . __crc_\(.*\)\$@\3\t0x\2\t${name}@")
	provided=$(echo -e "$provided\n$crc" | sort -u | sed '/^$/d')
}

# Arg: filename
collect_needed_versions()
{
	declare -i no=0
	if test ! -r $1; then
		echo "$1 not accessible"
		exit 8
	fi
	name=${1##*/}
	echo -n "Module $name: "
	while read ver sym; do
		ver=$(printf "0x%08x" $ver)
		match=$(echo "$provided" | filter $sym)
		#echo "$provided" | grep $sym
		#echo \"$sym\"
		#echo $match
		if test -z "$match"; then
			unresolved="$unresolved\n$sym\t$ver"
		else
			#verm=$(echo "$match" | head -n1 | sed 's/^[^ 	]*	\(0x[^ 	]*\).*$/\1/')
			verm=$(echo "$match" | sed 's/^[^ 	]*	\(0x[^ 	]*\).*$/\1/')
			if test "$ver" != "$verm"; then
				unmatched="$unmatched\n$sym\t$ver\t$verm"
			fi
		fi
		let no+=1
	done < <(modprobe --dump-modversions $1)
	# Consolidate
	unresolved=$(echo -e "$unresolved" | sort -u | sed '/^$/d')
	unmatched=$(echo -e "$unmatched" | sort -u | sed '/^$/d')
	# Find undefined unversioned symbols
	need=$(nm $1 | grep '^ *[uU]' | sed "s/^ *[uU] \(.*\)$/\1/")
	provfilt=$(echo "$provided" | sed 's/	.*$//' | sort -u)
	unrefilt=$(echo "$unresolved" | sed 's/	.*$//')
	need=$(echo "$need" | filter_out $provfilt)
	need=$(echo "$need" | filter_out $unrefilt)
	unresolved="$unresolved\n$need"
	unresolved=$(echo -e "$unresolved" | sort -u | sed '/^$/d')
	echo -n "$no imported versioned symbols, "
	echo -n $(echo "$need" | sed '/^$/d' | wc -l)
	echo " unversioned"
}

#main
if test -z "$2"; then
	echo "Usage: testmodule Versiondumpfile Module.ko [Module.ko [...]]"
	echo "Versiondumpfile is the file produced by collect_ksyms"
	echo " or the symvers file as produced in the build process;"
	echo " the script also accepts a kernel RPM as arg."
	exit 3
fi

# List of provided syms
case $1 in
	*.rpm)
		parse_symvers_from_rpm $1
		;;
	*)
		parse_symvers $1
		;;
esac		
shift
# Modules to test

for mod in $@; do
	add_exported_syms $mod
done
#echo "$provided"
for mod in $@; do
	collect_needed_versions $mod
done
# Summary
rv=0
echo "Unmatched symbol versions: $(echo "$unmatched" | sed '/^$/d' | wc -l)"
test -z "$unmatched" || { echo "$unmatched"; rv=1; }
echo "Unresolved symbols: $(echo "$unresolved" | sed '/^$/d' | wc -l)"
test -z "$unresolved" || { echo "$unresolved"; rv=2; }
exit $rv