2 # kabi - Linux Kernel Application Binary Interface manager
3 # Copyright (C) 2005 Cluster File Systems, Inc.
6 # Gordon Matzigkeit <gord@clusterfs.com>, 2005-10-21
13 my $CC = $ENV{'CC'} || 'gcc';
14 my $LINUX = '/usr/src/linux';
21 $progname =~ s/^.*\///;
22 my $modename = $progname;
28 print STDERR "Try \`$0 --help' for more information\n";
31 Usage: [CC=COMPILER] $0 [OPTION]... MODE ARGS...
33 Manage binary compatibility between a Linux kernel and kernel modules.
35 The CC environment variable specifies the compiler used to build the
38 --help display this message and exit
39 -o, --output=KABI specify the name of the KABI file created by the
40 \`module\' mode [default=strip .ko and add .kabi]
41 -v, --verbose give reasons for rejecting KABI matches
42 --version print version information
43 --with-linux=DIR set the path to the kernel sources
45 MODE and ARGS can be one of the following:
47 archive DIR KMOD install a KABI and kernel module in a unique place in DIR
48 match FILE... print a list of KABI files which are compatible with
49 the specified kernel and any specified kernel modules
51 module KMOD generate a KABI file for the specified kernel module
53 Written by Gordon Matzigkeit <gord\@clusterfs.com> for Cluster File Systems.
61 if ($args[0] =~ /^--with-l(i(n(u(x)?)?)?)?=(.*)/) {
63 } elsif ($args[0] =~ /^--with-l(i(n(u(x)?)?)?)?$/) {
66 } elsif ($args[0] =~ /^--h(e(l(p)?)?)?$/) {
68 } elsif ($args[0] =~ /^--vers(i(o(n)?)?)?$/) {
69 print "KABI $VERSION\n";
71 } elsif ($args[0] eq '-v' || $args[0] =~ /^--verb(o(s(e)?)?)?$/) {
73 } elsif ($args[0] =~ /^-/) {
74 print STDERR "$progname: unrecognized option \`$args[0]'\n";
76 } elsif (!defined $MODE) {
86 print STDERR "$progname: you must specify a MODE\n";
90 $modename .= ": $MODE";
91 if ($MODE eq 'archive') {
93 print STDERR "$modename: you must specify a DIR and KMOD\n";
97 my $ARCHIVE = $ARGS[0];
104 open(MD5SUM, "md5sum $KABI|") or
105 die "$modename: cannot execute \`md5sum': $!\n";
112 open(TAG, '<CVS/Tag') or
113 die "$modename: cannot read \`CVS/Tag': $!\n";
120 my ($dir, @sh_c, @cp);
121 if ($ARCHIVE =~ /^([^:][^:]+):(.*)$/) {
123 @sh_c = ('ssh', '-o', 'BatchMode=yes', $1);
127 @sh_c = ('sh', '-c');
131 system(@sh_c, "test -d $dir");
133 print STDERR "$modename: warning: \`$dir' is not reachable or does not exist\n";
137 print "archiving $KMOD in $ARCHIVE$TAG/$KMOD/$hash\n"
139 foreach my $d ("$dir$TAG", "$dir$TAG/$KMOD", "$dir$TAG/$KMOD/$hash") {
140 system(@sh_c, "test -d $d || mkdir $d");
145 system(@cp, $KMOD, $KABI, "$ARCHIVE$TAG/$KMOD/$hash");
147 } elsif ($MODE eq 'module') {
150 print STDERR "$modename: you must specify exactly one KMOD\n";
156 if (!defined $OUTPUT) {
158 $OUTPUT =~ s/\.k?o$//;
161 print "create $OUTPUT\n" if $VERBOSE;
162 open(OUT, ">$OUTPUT") or
163 die "$modename: cannot create \`$OUTPUT': $!\n";
165 my $outname = $OUTPUT;
166 $outname =~ s/^.*\///;
168 # $outname - Kernel module ABI descriptor file
169 # DO NOT EDIT - Automatically generated by $progname $VERSION
172 # Get the kernel version.
173 print OUT "kver " . kernel_version() . "\n";
175 # Gather the undefined symbols with version numbers from the
180 # Gather the version numbers, if any.
182 if ($modfile =~ s/\.ko$/.mod.c/) {
183 open(MOD, "<$modfile") or
184 die "$modename: cannot read \`$modfile': $!\n";
187 if (/\"__versions\"/) {
189 } elsif ($versions) {
190 if (/^\s*\{\s*(0x[0-9a-f]+)\s*,\s*\"([^\"]*)\"\s*\}\s*,\s*$/) {
193 } elsif (/^\s*\}\s*;\s*$/) {
200 open(NM, "nm $KMOD |") or
201 die "$modename: cannot execute \`nm $KMOD': $!\n";
203 if (/^\s*U\s*(.*\S)\s*$/) {
210 foreach my $undef (sort @undefs)
212 print OUT "usym $undef";
213 if (defined $vers{$undef}) {
214 print OUT " ", $vers{$undef};
220 die "$modename: cannot write \`$OUTPUT': $!\n";
222 } elsif ($MODE eq 'match') {
227 while ($#todo >= 0) {
229 if ($t =~ /\.kabi$/) {
232 # Add all the contents of the directory to our todo list.
234 while (my $ent = readdir(DIR)) {
237 } elsif (-d "$t/$ent") {
238 # Recurse into subdirectories.
239 unshift @todo, "$t/$ent";
240 } elsif ($ent =~ /\.k?o$/) {
241 # Add kernel modules.
242 unshift @todo, "$t/$ent";
247 # It's an explicit kernel module.
253 print STDERR "$modename: you must specify at least one KABI\n";
259 if (-f "$LINUX/Module.symvers") {
260 # Look up the version numbers in Module.symvers.
261 open(VERS, "<$LINUX/Module.symvers") or
262 die "$modename: cannot read \`$LINUX/Module.symvers': $!\n";
263 while ($_ = <VERS>) {
264 if (/^(0x[0-9a-f]+)\s+(\S+)/) {
265 $dsyms{$2} = hex($1);
270 # Read in all the non-versioned symbols defined by this kernel.
271 open(MAP, "<$LINUX/System.map") or
272 die "$modename: cannot read \`$LINUX/System.map': $!\n";
274 if (/^[0-9a-fA-F]*\s+[ABCDGIRSTW]+\s*(.*\S)\s*$/) {
281 # Find the symbols for the installed modules, too.
282 foreach my $mod (@KMODS) {
283 open(NM, "nm $mod |") or
284 die "$modename: cannot execute \`nm $mod': $!\n";
286 if (/^[0-9a-fA-F]*\s+[ABCDGIRSTW]+\s*(.*\S)\s*$/) {
293 # Also get the kernel version.
294 my $kver = kernel_version();
296 # Read each kabi file and print out the ones that are plausible
298 foreach my $kabi (@KABIS) {
299 open(KABI, "<$kabi") or
300 die "$modename: cannot read \`$kabi': $!\n";
302 while ($possible && ($_ = <KABI>)) {
305 } elsif (/^\s*kver\s+(.*\S)\s*$/) {
307 if ($modkver ne $kver) {
308 print STDERR "$kabi:$.: module version \`$modkver' differs from \`$kver'\n"
312 } elsif (/^\s*usym\s+(\S+)\s*(\S+)?\s*$/) {
313 my ($modsym, $symver) = ($1, hex($2));
314 if (!defined $dsyms{$modsym}) {
315 print STDERR "$kabi:$.: module symbol \`$modsym' is not defined\n"
318 } elsif (defined $symver && $dsyms{$modsym} != 0 && $dsyms{$modsym} != $symver) {
319 printf STDERR "$kabi:$.: module symbol \`$modsym' is version 0x%x, not 0x%x\n", $dsyms{$modsym}, $symver
323 } elsif (/^\s*(\S+)/) {
324 print STDERR "$kabi:$.: unrecognized descriptor line \`$1'\n";
336 print STDERR "$progname: unrecognized mode \`$MODE'\n";
341 # Read the kernel version from its built source tree.
344 my $verfile = "$LINUX/include/linux/version.h";
345 open(VERSION, "<$verfile") or
346 die "$modename: cannot read \`$verfile': $!\n";
349 while ($_ = <VERSION>) {
350 if (/^\s*#\s*define\s+UTS_RELEASE\s+"(.*)"\s*$/) {
359 die "$modename: cannot find UTS_RELEASE in \`$verfile'\n";