Whamcloud - gitweb
LU-98 Fix defects in bug 14949 implementation
[fs/lustre-release.git] / build / kabi
1 #! /usr/bin/perl
2 # kabi - Linux Kernel Application Binary Interface manager
3 #
4 # Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
5 # Use is subject to license terms.
6 #
7 # Gordon Matzigkeit <gord@clusterfs.com>, 2005-10-21
8
9 use warnings;
10 use strict;
11
12 my $VERSION = '0.2';
13
14 my $CC = $ENV{'CC'} || 'gcc';
15 my $LINUX = '/usr/src/linux';
16 my $MODE;
17 my $OUTPUT;
18 my @ARGS;
19 my $VERBOSE = 0;
20
21 my $progname = $0;
22 $progname =~ s/^.*\///;
23 my $modename = $progname;
24
25 sub usage
26 {
27     my ($status) = @_;
28     if ($status) {
29         print STDERR "Try \`$0 --help' for more information\n";
30     } else {
31         print <<EOF;
32 Usage: [CC=COMPILER] $0 [OPTION]... MODE ARGS...
33
34 Manage binary compatibility between a Linux kernel and kernel modules.
35
36 The CC environment variable specifies the compiler used to build the
37 kernel and modules.
38
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
45
46 MODE and ARGS can be one of the following:
47
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
51
52     module KMOD       generate a KABI file for the specified kernel module
53
54 Written by Gordon Matzigkeit <gord\@clusterfs.com> for Sun Microsystems, Inc.
55 EOF
56     }
57     exit $status;
58 }
59
60 my @args = @ARGV;
61 while ($#args >= 0) {
62     if ($args[0] =~ /^--with-l(i(n(u(x)?)?)?)?=(.*)/) {
63         $LINUX = $5;
64     } elsif ($args[0] =~ /^--with-l(i(n(u(x)?)?)?)?$/) {
65         shift @args;
66         $LINUX = $args[0];
67     } elsif ($args[0] =~ /^--h(e(l(p)?)?)?$/) {
68         usage(0);
69     } elsif ($args[0] =~ /^--vers(i(o(n)?)?)?$/) {
70         print "KABI $VERSION\n";
71         exit 0;
72     } elsif ($args[0] eq '-v' || $args[0] =~ /^--verb(o(s(e)?)?)?$/) {
73         $VERBOSE = 1;
74     } elsif ($args[0] =~ /^-/) {
75         print STDERR "$progname: unrecognized option \`$args[0]'\n";
76         usage(1);
77     } elsif (!defined $MODE) {
78         $MODE = $args[0];
79     } else {
80         push @ARGS, $args[0];
81     }
82     shift @args;
83 }
84
85
86 if (!defined $MODE) {
87     print STDERR "$progname: you must specify a MODE\n";
88     usage(1);
89 }
90
91 $modename .= ": $MODE";
92 if ($MODE eq 'archive') {
93     if ($#ARGS != 1) {
94         print STDERR "$modename: you must specify a DIR and KMOD\n";
95         usage(1);
96     }
97
98     my $ARCHIVE = $ARGS[0];
99     my $KMOD = $ARGS[1];
100
101     my $KABI = $KMOD;
102     $KABI =~ s/\.k?o$//;
103     $KABI .= '.kabi';
104
105     open(MD5SUM, "md5sum $KABI|") or
106         die "$modename: cannot execute \`md5sum': $!\n";
107     my $hash = <MD5SUM>;
108     close(MD5SUM);
109     $hash =~ s/\s+.*//s;
110
111     my $TAG = '';
112     if (-d 'CVS') {
113         open(TAG, '<CVS/Tag') or
114             die "$modename: cannot read \`CVS/Tag': $!\n";
115         $TAG = <TAG>;
116         close(TAG);
117         chomp $TAG;
118         $TAG = "/$TAG";
119     }
120
121     my ($dir, @sh_c, @cp);
122     if ($ARCHIVE =~ /^([^:][^:]+):(.*)$/) {
123         $dir = $2;
124         @sh_c = ('ssh', '-o', 'BatchMode=yes', $1);
125         @cp = ('scp', '-B');
126     } else {
127         $dir = $ARCHIVE;
128         @sh_c = ('sh', '-c');
129         @cp = ('cp');
130     }
131
132     system(@sh_c, "test -d $dir");
133     if ($? >> 8 != 0) {
134         print STDERR "$modename: warning: \`$dir' is not reachable or does not exist\n";
135         exit 0;
136     }
137
138     print "archiving $KMOD in $ARCHIVE$TAG/$KMOD/$hash\n"
139         if $VERBOSE;
140     foreach my $d ("$dir$TAG", "$dir$TAG/$KMOD", "$dir$TAG/$KMOD/$hash") {
141         system(@sh_c, "test -d $d || mkdir $d");
142         if ($? >> 8 != 0) {
143             exit $? >> 8;
144         }
145     }
146     system(@cp, $KMOD, $KABI, "$ARCHIVE$TAG/$KMOD/$hash");
147     exit $? >> 8;
148 } elsif ($MODE eq 'module') {
149     
150     if ($#ARGS != 0) {
151         print STDERR "$modename: you must specify exactly one KMOD\n";
152         usage(1);
153     }
154
155     my $KMOD = $ARGS[0];
156
157     if (!defined $OUTPUT) {
158         $OUTPUT = $KMOD;
159         $OUTPUT =~ s/\.k?o$//;
160         $OUTPUT .= '.kabi';
161     }
162     print "create $OUTPUT\n" if $VERBOSE;
163     open(OUT, ">$OUTPUT") or
164         die "$modename: cannot create \`$OUTPUT': $!\n";
165
166     my $outname = $OUTPUT;
167     $outname =~ s/^.*\///;
168     print OUT <<EOF;
169 # $outname - Kernel module ABI descriptor file
170 # DO NOT EDIT - Automatically generated by $progname $VERSION
171 EOF
172
173     # Get the kernel version.
174     print OUT "kver " . kernel_version() . "\n";
175
176     # Gather the undefined symbols with version numbers from the
177     # kernel module.
178     my %vers;
179     my @undefs;
180
181     # Gather the version numbers, if any.
182     my $modfile = $KMOD;
183     if ($modfile =~ s/\.ko$/.mod.c/) {
184         open(MOD, "<$modfile") or
185             die "$modename: cannot read \`$modfile': $!\n";
186         my $versions = 0;
187         while ($_ = <MOD>) {
188             if (/\"__versions\"/) {
189                 $versions = 1;
190             } elsif ($versions) {
191                 if (/^\s*\{\s*(0x[0-9a-f]+)\s*,\s*\"([^\"]*)\"\s*\}\s*,\s*$/) {
192                     $vers{$2} = $1;
193                     push(@undefs, $2);
194                 } elsif (/^\s*\}\s*;\s*$/) {
195                     $versions = 0;
196                 }
197             }
198         }
199         close(MOD);
200     } else {
201         open(NM, "nm $KMOD |") or
202             die "$modename: cannot execute \`nm $KMOD': $!\n";
203         while ($_ = <NM>) {
204             if (/^\s*U\s*(.*\S)\s*$/) {
205                 push @undefs, $1;
206             }
207         }
208         close(NM);
209     }
210
211     foreach my $undef (sort @undefs)
212     {
213         print OUT "usym $undef";
214         if (defined $vers{$undef}) {
215             print OUT " ", $vers{$undef};
216         }
217         print OUT "\n";
218     }
219
220     close(OUT) or
221         die "$modename: cannot write \`$OUTPUT': $!\n";
222
223 } elsif ($MODE eq 'match') {
224     my @KABIS;
225     my @KMODS;
226
227     my @todo = @ARGS;
228     while ($#todo >= 0) {
229         my $t = shift @todo;
230         if ($t =~ /\.kabi$/) {
231             push @KABIS, $t;
232         } elsif (-d $t) {
233             # Add all the contents of the directory to our todo list.
234             opendir(DIR, $t);
235             while (my $ent = readdir(DIR)) {
236                 if ($ent =~ /^\./) {
237                     # Skip dotfiles.
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";
244                 }
245             }
246             closedir(DIR);
247         } else {
248             # It's an explicit kernel module.
249             push @KMODS, $t;
250         }
251     }
252
253     if ($#KABIS < 0) {
254         print STDERR "$modename: you must specify at least one KABI\n";
255         usage(1);
256     }
257
258     my %dsyms;
259
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);
267             }
268         }
269         close(VERS);
270     } else {
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";
274         while ($_ = <MAP>) {
275             if (/^[0-9a-fA-F]*\s+[ABCDGIRSTW]+\s*(.*\S)\s*$/) {
276                 $dsyms{$1} = 0;
277             }
278         }
279         close(MAP);
280     }
281
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";
286         while ($_ = <NM>) {
287             if (/^[0-9a-fA-F]*\s+[ABCDGIRSTW]+\s*(.*\S)\s*$/) {
288                 $dsyms{$1} = 0;
289             }
290         }
291         close(NM);
292     }
293
294     # Also get the kernel version.
295     my $kver = kernel_version();
296
297     # Read each kabi file and print out the ones that are plausible
298     # matches.
299     foreach my $kabi (@KABIS) {
300         open(KABI, "<$kabi") or
301             die "$modename: cannot read \`$kabi': $!\n";
302         my $possible = 1;
303         while ($possible && ($_ = <KABI>)) {
304             if (/^\s*#/) {
305                 # Skip comments.
306             } elsif (/^\s*kver\s+(.*\S)\s*$/) {
307                 my $modkver = $1;
308                 if ($modkver ne $kver) {
309                     print STDERR "$kabi:$.: module version \`$modkver' differs from \`$kver'\n"
310                         if $VERBOSE;
311                     $possible = 0;
312                 }
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"
317                         if $VERBOSE;
318                     $possible = 0;
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
321                         if $VERBOSE;
322                     $possible = 0;
323                 }
324             } elsif (/^\s*(\S+)/) {
325                 print STDERR "$kabi:$.: unrecognized descriptor line \`$1'\n";
326             }
327         }
328         close(KABI);
329         
330         if ($possible) {
331             # We got a match.
332             print "$kabi\n";
333         }
334     }
335
336 } else {
337     print STDERR "$progname: unrecognized mode \`$MODE'\n";
338     usage(1);
339 }
340
341
342 # Read the kernel version from its built source tree.
343 sub kernel_version
344 {
345     my $verfile = "$LINUX/include/linux/version.h";
346     open(VERSION, "<$verfile") or
347         die "$modename: cannot read \`$verfile': $!\n";
348
349     my $ver;
350     while ($_ = <VERSION>) {
351         if (/^\s*#\s*define\s+UTS_RELEASE\s+"(.*)"\s*$/) {
352             $ver = $1;
353             last;
354         }
355     }
356
357     close(VERSION);
358
359     if (!defined $ver) {
360         die "$modename: cannot find UTS_RELEASE in \`$verfile'\n";
361     }
362     return "linux-$ver";
363 }
364
365 exit 0;