2 # kabi - Linux Kernel Application Binary Interface manager
4 # Copyright 2008 Sun Microsystems, Inc. All rights reserved
5 # Use is subject to license terms.
7 # Gordon Matzigkeit <gord@clusterfs.com>, 2005-10-21
14 my $CC = $ENV{'CC'} || 'gcc';
15 my $LINUX = '/usr/src/linux';
22 $progname =~ s/^.*\///;
23 my $modename = $progname;
29 print STDERR "Try \`$0 --help' for more information\n";
32 Usage: [CC=COMPILER] $0 [OPTION]... MODE ARGS...
34 Manage binary compatibility between a Linux kernel and kernel modules.
36 The CC environment variable specifies the compiler used to build the
39 --help display this message and exit
40 -o, --output=KABI specify the name of the KABI file created by the
41 \`module\' mode [default=strip .ko and add .kabi]
42 -v, --verbose give reasons for rejecting KABI matches
43 --version print version information
44 --with-linux=DIR set the path to the kernel sources
46 MODE and ARGS can be one of the following:
48 archive DIR KMOD install a KABI and kernel module in a unique place in DIR
49 match FILE... print a list of KABI files which are compatible with
50 the specified kernel and any specified kernel modules
52 module KMOD generate a KABI file for the specified kernel module
54 Written by Gordon Matzigkeit <gord\@clusterfs.com> for Sun Microsystems, Inc.
62 if ($args[0] =~ /^--with-l(i(n(u(x)?)?)?)?=(.*)/) {
64 } elsif ($args[0] =~ /^--with-l(i(n(u(x)?)?)?)?$/) {
67 } elsif ($args[0] =~ /^--h(e(l(p)?)?)?$/) {
69 } elsif ($args[0] =~ /^--vers(i(o(n)?)?)?$/) {
70 print "KABI $VERSION\n";
72 } elsif ($args[0] eq '-v' || $args[0] =~ /^--verb(o(s(e)?)?)?$/) {
74 } elsif ($args[0] =~ /^-/) {
75 print STDERR "$progname: unrecognized option \`$args[0]'\n";
77 } elsif (!defined $MODE) {
87 print STDERR "$progname: you must specify a MODE\n";
91 $modename .= ": $MODE";
92 if ($MODE eq 'archive') {
94 print STDERR "$modename: you must specify a DIR and KMOD\n";
98 my $ARCHIVE = $ARGS[0];
105 open(MD5SUM, "md5sum $KABI|") or
106 die "$modename: cannot execute \`md5sum': $!\n";
113 open(TAG, '<CVS/Tag') or
114 die "$modename: cannot read \`CVS/Tag': $!\n";
121 my ($dir, @sh_c, @cp);
122 if ($ARCHIVE =~ /^([^:][^:]+):(.*)$/) {
124 @sh_c = ('ssh', '-o', 'BatchMode=yes', $1);
128 @sh_c = ('sh', '-c');
132 system(@sh_c, "test -d $dir");
134 print STDERR "$modename: warning: \`$dir' is not reachable or does not exist\n";
138 print "archiving $KMOD in $ARCHIVE$TAG/$KMOD/$hash\n"
140 foreach my $d ("$dir$TAG", "$dir$TAG/$KMOD", "$dir$TAG/$KMOD/$hash") {
141 system(@sh_c, "test -d $d || mkdir $d");
146 system(@cp, $KMOD, $KABI, "$ARCHIVE$TAG/$KMOD/$hash");
148 } elsif ($MODE eq 'module') {
151 print STDERR "$modename: you must specify exactly one KMOD\n";
157 if (!defined $OUTPUT) {
159 $OUTPUT =~ s/\.k?o$//;
162 print "create $OUTPUT\n" if $VERBOSE;
163 open(OUT, ">$OUTPUT") or
164 die "$modename: cannot create \`$OUTPUT': $!\n";
166 my $outname = $OUTPUT;
167 $outname =~ s/^.*\///;
169 # $outname - Kernel module ABI descriptor file
170 # DO NOT EDIT - Automatically generated by $progname $VERSION
173 # Get the kernel version.
174 print OUT "kver " . kernel_version() . "\n";
176 # Gather the undefined symbols with version numbers from the
181 # Gather the version numbers, if any.
183 if ($modfile =~ s/\.ko$/.mod.c/) {
184 open(MOD, "<$modfile") or
185 die "$modename: cannot read \`$modfile': $!\n";
188 if (/\"__versions\"/) {
190 } elsif ($versions) {
191 if (/^\s*\{\s*(0x[0-9a-f]+)\s*,\s*\"([^\"]*)\"\s*\}\s*,\s*$/) {
194 } elsif (/^\s*\}\s*;\s*$/) {
201 open(NM, "nm $KMOD |") or
202 die "$modename: cannot execute \`nm $KMOD': $!\n";
204 if (/^\s*U\s*(.*\S)\s*$/) {
211 foreach my $undef (sort @undefs)
213 print OUT "usym $undef";
214 if (defined $vers{$undef}) {
215 print OUT " ", $vers{$undef};
221 die "$modename: cannot write \`$OUTPUT': $!\n";
223 } elsif ($MODE eq 'match') {
228 while ($#todo >= 0) {
230 if ($t =~ /\.kabi$/) {
233 # Add all the contents of the directory to our todo list.
235 while (my $ent = readdir(DIR)) {
238 } elsif (-d "$t/$ent") {
239 # Recurse into subdirectories.
240 unshift @todo, "$t/$ent";
241 } elsif ($ent =~ /\.k?o$/) {
242 # Add kernel modules.
243 unshift @todo, "$t/$ent";
248 # It's an explicit kernel module.
254 print STDERR "$modename: you must specify at least one KABI\n";
260 if (-f "$LINUX/Module.symvers") {
261 # Look up the version numbers in Module.symvers.
262 open(VERS, "<$LINUX/Module.symvers") or
263 die "$modename: cannot read \`$LINUX/Module.symvers': $!\n";
264 while ($_ = <VERS>) {
265 if (/^(0x[0-9a-f]+)\s+(\S+)/) {
266 $dsyms{$2} = hex($1);
271 # Read in all the non-versioned symbols defined by this kernel.
272 open(MAP, "<$LINUX/System.map") or
273 die "$modename: cannot read \`$LINUX/System.map': $!\n";
275 if (/^[0-9a-fA-F]*\s+[ABCDGIRSTW]+\s*(.*\S)\s*$/) {
282 # Find the symbols for the installed modules, too.
283 foreach my $mod (@KMODS) {
284 open(NM, "nm $mod |") or
285 die "$modename: cannot execute \`nm $mod': $!\n";
287 if (/^[0-9a-fA-F]*\s+[ABCDGIRSTW]+\s*(.*\S)\s*$/) {
294 # Also get the kernel version.
295 my $kver = kernel_version();
297 # Read each kabi file and print out the ones that are plausible
299 foreach my $kabi (@KABIS) {
300 open(KABI, "<$kabi") or
301 die "$modename: cannot read \`$kabi': $!\n";
303 while ($possible && ($_ = <KABI>)) {
306 } elsif (/^\s*kver\s+(.*\S)\s*$/) {
308 if ($modkver ne $kver) {
309 print STDERR "$kabi:$.: module version \`$modkver' differs from \`$kver'\n"
313 } elsif (/^\s*usym\s+(\S+)\s*(\S+)?\s*$/) {
314 my ($modsym, $symver) = ($1, hex($2));
315 if (!defined $dsyms{$modsym}) {
316 print STDERR "$kabi:$.: module symbol \`$modsym' is not defined\n"
319 } elsif (defined $symver && $dsyms{$modsym} != 0 && $dsyms{$modsym} != $symver) {
320 printf STDERR "$kabi:$.: module symbol \`$modsym' is version 0x%x, not 0x%x\n", $dsyms{$modsym}, $symver
324 } elsif (/^\s*(\S+)/) {
325 print STDERR "$kabi:$.: unrecognized descriptor line \`$1'\n";
337 print STDERR "$progname: unrecognized mode \`$MODE'\n";
342 # Read the kernel version from its built source tree.
345 my $verfile = "$LINUX/include/linux/version.h";
346 open(VERSION, "<$verfile") or
347 die "$modename: cannot read \`$verfile': $!\n";
350 while ($_ = <VERSION>) {
351 if (/^\s*#\s*define\s+UTS_RELEASE\s+"(.*)"\s*$/) {
360 die "$modename: cannot find UTS_RELEASE in \`$verfile'\n";