Whamcloud - gitweb
Add kabi script to CVS.
authorgord <gord>
Wed, 9 Nov 2005 20:47:25 +0000 (20:47 +0000)
committergord <gord>
Wed, 9 Nov 2005 20:47:25 +0000 (20:47 +0000)
build/Rules.in
build/kabi [new file with mode: 0755]

index 19862f2..f03bb80 100644 (file)
@@ -23,6 +23,31 @@ ifeq ($(PATCHLEVEL),)
 
 include autoMakefile
 
 
 include autoMakefile
 
+# The kernel ABI files for the nonfree modules.
+KABIS := $(NONFREE_MODULES:%$(KMODEXT)=%.kabi)
+
+all: archive-nonfree-modules
+
+# Where to archive the nonfree modules for binary distribution.
+# If this directory has a colon in it, SSH/SCP are used to go out on the network.
+nonfreedir := $$HOME/nonfree
+#nonfreedir := moraine.clusterfs.com:/home/lustre-nonfree
+
+# Put the nonfree modules and corresponding KABI files into the binary
+# archive.  We assume that if the CVS subdirectory doesn't exist, we
+# don't want to archive.
+archive-nonfree-modules: $(KABIS) $(NONFREE_MODULES)
+       test -d CVS || exit 0; \
+       list="$(NONFREE_MODULES)"; for mod in $$list; do \
+         perl $(top_srcdir)/build/kabi -v archive $(nonfreedir) $$mod || exit $$?; \
+       done
+
+# Generate the Kernel ABI files for the nonfree modules.
+$(KABIS): $(NONFREE_MODULES)
+       for mod in $(NONFREE_MODULES); do \
+         CC="$(CC)" perl $(top_srcdir)/build/kabi --with-linux="$(LINUX)" module $$mod || exit $$?; \
+       done
+
 fix-kext-ownership:
        @if test -d $(DESTDIR)$(kextdir) ; then \
                echo chown -R root:wheel $(DESTDIR)$(kextdir) ; \
 fix-kext-ownership:
        @if test -d $(DESTDIR)$(kextdir) ; then \
                echo chown -R root:wheel $(DESTDIR)$(kextdir) ; \
diff --git a/build/kabi b/build/kabi
new file mode 100755 (executable)
index 0000000..a57a46c
--- /dev/null
@@ -0,0 +1,364 @@
+#! /usr/bin/perl
+# kabi - Linux Kernel Application Binary Interface manager
+# Copyright (C) 2005  Cluster File Systems, Inc.
+# All rights reserved.
+#
+# Gordon Matzigkeit <gord@clusterfs.com>, 2005-10-21
+
+use warnings;
+use strict;
+
+my $VERSION = '0.2';
+
+my $CC = $ENV{'CC'} || 'gcc';
+my $LINUX = '/usr/src/linux';
+my $MODE;
+my $OUTPUT;
+my @ARGS;
+my $VERBOSE = 0;
+
+my $progname = $0;
+$progname =~ s/^.*\///;
+my $modename = $progname;
+
+sub usage
+{
+    my ($status) = @_;
+    if ($status) {
+       print STDERR "Try \`$0 --help' for more information\n";
+    } else {
+       print <<EOF;
+Usage: [CC=COMPILER] $0 [OPTION]... MODE ARGS...
+
+Manage binary compatibility between a Linux kernel and kernel modules.
+
+The CC environment variable specifies the compiler used to build the
+kernel and modules.
+
+    --help            display this message and exit
+-o, --output=KABI     specify the name of the KABI file created by the
+                      \`module\' mode [default=strip .ko and add .kabi]
+-v, --verbose         give reasons for rejecting KABI matches
+    --version         print version information
+    --with-linux=DIR  set the path to the kernel sources
+
+MODE and ARGS can be one of the following:
+
+    archive DIR KMOD  install a KABI and kernel module in a unique place in DIR
+    match FILE...     print a list of KABI files which are compatible with
+                      the specified kernel and any specified kernel modules
+
+    module KMOD       generate a KABI file for the specified kernel module
+
+Written by Gordon Matzigkeit <gord\@clusterfs.com> for Cluster File Systems.
+EOF
+    }
+    exit $status;
+}
+
+my @args = @ARGV;
+while ($#args >= 0) {
+    if ($args[0] =~ /^--with-l(i(n(u(x)?)?)?)?=(.*)/) {
+       $LINUX = $5;
+    } elsif ($args[0] =~ /^--with-l(i(n(u(x)?)?)?)?$/) {
+       shift @args;
+       $LINUX = $args[0];
+    } elsif ($args[0] =~ /^--h(e(l(p)?)?)?$/) {
+       usage(0);
+    } elsif ($args[0] =~ /^--vers(i(o(n)?)?)?$/) {
+       print "KABI $VERSION\n";
+       exit 0;
+    } elsif ($args[0] eq '-v' || $args[0] =~ /^--verb(o(s(e)?)?)?$/) {
+       $VERBOSE = 1;
+    } elsif ($args[0] =~ /^-/) {
+       print STDERR "$progname: unrecognized option \`$args[0]'\n";
+       usage(1);
+    } elsif (!defined $MODE) {
+       $MODE = $args[0];
+    } else {
+       push @ARGS, $args[0];
+    }
+    shift @args;
+}
+
+
+if (!defined $MODE) {
+    print STDERR "$progname: you must specify a MODE\n";
+    usage(1);
+}
+
+$modename .= ": $MODE";
+if ($MODE eq 'archive') {
+    if ($#ARGS != 1) {
+       print STDERR "$modename: you must specify a DIR and KMOD\n";
+       usage(1);
+    }
+
+    my $ARCHIVE = $ARGS[0];
+    my $KMOD = $ARGS[1];
+
+    my $KABI = $KMOD;
+    $KABI =~ s/\.k?o$//;
+    $KABI .= '.kabi';
+
+    open(MD5SUM, "md5sum $KABI|") or
+       die "$modename: cannot execute \`md5sum': $!\n";
+    my $hash = <MD5SUM>;
+    close(MD5SUM);
+    $hash =~ s/\s+.*//s;
+
+    my $TAG = '';
+    if (-d 'CVS') {
+       open(TAG, '<CVS/Tag') or
+           die "$modename: cannot read \`CVS/Tag': $!\n";
+       $TAG = <TAG>;
+       close(TAG);
+       chomp $TAG;
+       $TAG = "/$TAG";
+    }
+
+    my ($dir, @sh_c, @cp);
+    if ($ARCHIVE =~ /^([^:][^:]+):(.*)$/) {
+       $dir = $2;
+       @sh_c = ('ssh', '-o', 'BatchMode=yes', $1);
+       @cp = ('scp', '-B');
+    } else {
+       $dir = $ARCHIVE;
+       @sh_c = ('sh', '-c');
+       @cp = ('cp');
+    }
+
+    system(@sh_c, "test -d $dir");
+    if ($? >> 8 != 0) {
+       print STDERR "$modename: warning: \`$dir' is not reachable or does not exist\n";
+       exit 0;
+    }
+
+    print "archiving $KMOD in $ARCHIVE$TAG/$KMOD/$hash\n"
+       if $VERBOSE;
+    foreach my $d ("$dir$TAG", "$dir$TAG/$KMOD", "$dir$TAG/$KMOD/$hash") {
+       system(@sh_c, "test -d $d || mkdir $d");
+       if ($? >> 8 != 0) {
+           exit $? >> 8;
+       }
+    }
+    system(@cp, $KMOD, $KABI, "$ARCHIVE$TAG/$KMOD/$hash");
+    exit $? >> 8;
+} elsif ($MODE eq 'module') {
+    
+    if ($#ARGS != 0) {
+       print STDERR "$modename: you must specify exactly one KMOD\n";
+       usage(1);
+    }
+
+    my $KMOD = $ARGS[0];
+
+    if (!defined $OUTPUT) {
+       $OUTPUT = $KMOD;
+       $OUTPUT =~ s/\.k?o$//;
+       $OUTPUT .= '.kabi';
+    }
+    print "create $OUTPUT\n" if $VERBOSE;
+    open(OUT, ">$OUTPUT") or
+       die "$modename: cannot create \`$OUTPUT': $!\n";
+
+    my $outname = $OUTPUT;
+    $outname =~ s/^.*\///;
+    print OUT <<EOF;
+# $outname - Kernel module ABI descriptor file
+# DO NOT EDIT - Automatically generated by $progname $VERSION
+EOF
+
+    # Get the kernel version.
+    print OUT "kver " . kernel_version() . "\n";
+
+    # Gather the undefined symbols with version numbers from the
+    # kernel module.
+    my %vers;
+    my @undefs;
+
+    # Gather the version numbers, if any.
+    my $modfile = $KMOD;
+    if ($modfile =~ s/\.ko$/.mod.c/) {
+       open(MOD, "<$modfile") or
+           die "$modename: cannot read \`$modfile': $!\n";
+       my $versions = 0;
+       while ($_ = <MOD>) {
+           if (/\"__versions\"/) {
+               $versions = 1;
+           } elsif ($versions) {
+               if (/^\s*\{\s*(0x[0-9a-f]+)\s*,\s*\"([^\"]*)\"\s*\}\s*,\s*$/) {
+                   $vers{$2} = $1;
+                   push(@undefs, $2);
+               } elsif (/^\s*\}\s*;\s*$/) {
+                   $versions = 0;
+               }
+           }
+       }
+       close(MOD);
+    } else {
+       open(NM, "nm $KMOD |") or
+           die "$modename: cannot execute \`nm $KMOD': $!\n";
+       while ($_ = <NM>) {
+           if (/^\s*U\s*(.*\S)\s*$/) {
+               push @undefs, $1;
+           }
+       }
+       close(NM);
+    }
+
+    foreach my $undef (sort @undefs)
+    {
+       print OUT "usym $undef";
+       if (defined $vers{$undef}) {
+           print OUT " ", $vers{$undef};
+       }
+       print OUT "\n";
+    }
+
+    close(OUT) or
+       die "$modename: cannot write \`$OUTPUT': $!\n";
+
+} elsif ($MODE eq 'match') {
+    my @KABIS;
+    my @KMODS;
+
+    my @todo = @ARGS;
+    while ($#todo >= 0) {
+       my $t = shift @todo;
+       if ($t =~ /\.kabi$/) {
+           push @KABIS, $t;
+       } elsif (-d $t) {
+           # Add all the contents of the directory to our todo list.
+           opendir(DIR, $t);
+           while (my $ent = readdir(DIR)) {
+               if ($ent =~ /^\./) {
+                   # Skip dotfiles.
+               } elsif (-d "$t/$ent") {
+                   # Recurse into subdirectories.
+                   unshift @todo, "$t/$ent";
+               } elsif ($ent =~ /\.k?o$/) {
+                   # Add kernel modules.
+                   unshift @todo, "$t/$ent";
+               }
+           }
+           closedir(DIR);
+       } else {
+           # It's an explicit kernel module.
+           push @KMODS, $t;
+       }
+    }
+
+    if ($#KABIS < 0) {
+       print STDERR "$modename: you must specify at least one KABI\n";
+       usage(1);
+    }
+
+    my %dsyms;
+
+    if (-f "$LINUX/Module.symvers") {
+       # Look up the version numbers in Module.symvers.
+       open(VERS, "<$LINUX/Module.symvers") or
+           die "$modename: cannot read \`$LINUX/Module.symvers': $!\n";
+       while ($_ = <VERS>) {
+           if (/^(0x[0-9a-f]+)\s+(\S+)/) {
+               $dsyms{$2} = hex($1);
+           }
+       }
+       close(VERS);
+    } else {
+       # Read in all the non-versioned symbols defined by this kernel.
+       open(MAP, "<$LINUX/System.map") or
+           die "$modename: cannot read \`$LINUX/System.map': $!\n";
+       while ($_ = <MAP>) {
+           if (/^[0-9a-fA-F]*\s+[ABCDGIRSTW]+\s*(.*\S)\s*$/) {
+               $dsyms{$1} = 0;
+           }
+       }
+       close(MAP);
+    }
+
+    # Find the symbols for the installed modules, too.
+    foreach my $mod (@KMODS) {
+       open(NM, "nm $mod |") or
+           die "$modename: cannot execute \`nm $mod': $!\n";
+       while ($_ = <NM>) {
+           if (/^[0-9a-fA-F]*\s+[ABCDGIRSTW]+\s*(.*\S)\s*$/) {
+               $dsyms{$1} = 0;
+           }
+       }
+       close(NM);
+    }
+
+    # Also get the kernel version.
+    my $kver = kernel_version();
+
+    # Read each kabi file and print out the ones that are plausible
+    # matches.
+    foreach my $kabi (@KABIS) {
+       open(KABI, "<$kabi") or
+           die "$modename: cannot read \`$kabi': $!\n";
+       my $possible = 1;
+       while ($possible && ($_ = <KABI>)) {
+           if (/^\s*#/) {
+               # Skip comments.
+           } elsif (/^\s*kver\s+(.*\S)\s*$/) {
+               my $modkver = $1;
+               if ($modkver ne $kver) {
+                   print STDERR "$kabi:$.: module version \`$modkver' differs from \`$kver'\n"
+                       if $VERBOSE;
+                   $possible = 0;
+               }
+           } elsif (/^\s*usym\s+(\S+)\s*(\S+)?\s*$/) {
+               my ($modsym, $symver) = ($1, hex($2));
+               if (!defined $dsyms{$modsym}) {
+                   print STDERR "$kabi:$.: module symbol \`$modsym' is not defined\n"
+                       if $VERBOSE;
+                   $possible = 0;
+               } elsif (defined $symver && $dsyms{$modsym} != 0 && $dsyms{$modsym} != $symver) {
+                   printf STDERR "$kabi:$.: module symbol \`$modsym' is version 0x%x, not 0x%x\n", $dsyms{$modsym}, $symver
+                       if $VERBOSE;
+                   $possible = 0;
+               }
+           } elsif (/^\s*(\S+)/) {
+               print STDERR "$kabi:$.: unrecognized descriptor line \`$1'\n";
+           }
+       }
+       close(KABI);
+       
+       if ($possible) {
+           # We got a match.
+           print "$kabi\n";
+       }
+    }
+
+} else {
+    print STDERR "$progname: unrecognized mode \`$MODE'\n";
+    usage(1);
+}
+
+
+# Read the kernel version from its built source tree.
+sub kernel_version
+{
+    my $verfile = "$LINUX/include/linux/version.h";
+    open(VERSION, "<$verfile") or
+       die "$modename: cannot read \`$verfile': $!\n";
+
+    my $ver;
+    while ($_ = <VERSION>) {
+       if (/^\s*#\s*define\s+UTS_RELEASE\s+"(.*)"\s*$/) {
+           $ver = $1;
+           last;
+       }
+    }
+
+    close(VERSION);
+
+    if (!defined $ver) {
+       die "$modename: cannot find UTS_RELEASE in \`$verfile'\n";
+    }
+    return "linux-$ver";
+}
+
+exit 0;