1#!/usr/bin/perl -w
2# vi: set sw=4 ts=4:
3# Copyright (c) 2001 David Schleef <ds@schleef.org>
4# Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
5# Copyright (c) 2001 Stuart Hughes <seh@zee2.com>
6# Copyright (c) 2002 Steven J. Hill <shill@broadcom.com>
7# Copyright (c) 2006 Freescale Semiconductor, Inc <stuarth@freescale.com>
8#
9# History:
10# March 2006: Stuart Hughes <stuarth@freescale.com>.
11#             Significant updates, including implementing the '-F' option
12#             and adding support for 2.6 kernels.
13
14# This program is free software; you can redistribute it and/or modify it
15# under the same terms as Perl itself.
16use Getopt::Long qw(:config no_auto_abbrev no_ignore_case);
17use File::Find;
18use strict;
19
20# Set up some default values
21my $kdir="";
22my $basedir="";
23my $kernel="";
24my $kernelsyms="";
25my $symprefix="";
26my $all=0;
27my $quick=0;
28my $errsyms=0;
29my $stdout=0;
30my $verbose=0;
31my $help=0;
32my $nm = $ENV{'NM'} || "nm";
33
34# more globals
35my (@liblist) = ();
36my $exp = {};
37my $dep = {};
38my $mod = {};
39
40my $usage = <<TXT;
41$0 -b basedir { -k <vmlinux> | -F <System.map> } [options]...
42  Where:
43   -h --help          : Show this help screen
44   -b --basedir       : Modules base directory (e.g /lib/modules/<2.x.y>)
45   -k --kernel        : Kernel binary for the target (e.g. vmlinux)
46   -F --kernelsyms    : Kernel symbol file (e.g. System.map)
47   -n --stdout        : Write to stdout instead of <basedir>/modules.dep
48   -v --verbose       : Print out lots of debugging stuff
49   -P --symbol-prefix : Symbol prefix
50   -a --all           : Probe all modules (default/only thing supported)
51   -e --errsyms       : Report any symbols not supplied by modules/kernel
52TXT
53
54# get command-line options
55GetOptions(
56	"help|h"            => \$help,
57	"basedir|b=s"       => \$basedir,
58	"kernel|k=s"        => \$kernel,
59	"kernelsyms|F=s"    => \$kernelsyms,
60	"stdout|n"          => \$stdout,
61	"verbose|v"         => \$verbose,
62	"symbol-prefix|P=s" => \$symprefix,
63	"all|a"             => \$all,
64	# unsupported options
65	"quick|A"           => \$quick,
66	# ignored options (for historical usage)
67	"quiet|q",
68	"root|r",
69	"unresolved-error|u"
70);
71
72die $usage if $help;
73die $usage unless $basedir && ( $kernel || $kernelsyms );
74die "can't use both -k and -F\n\n$usage" if $kernel && $kernelsyms;
75die "sorry, -A/--quick is not supported" if $quick;
76die "--errsyms requires --kernelsyms" if $errsyms && !$kernelsyms;
77
78# Strip any trailing or multiple slashes from basedir
79$basedir =~ s-/+$--g;
80
81# The base directory should contain /lib/modules somewhere
82if($basedir !~ m-/lib/modules-) {
83    warn "WARNING: base directory does not match ..../lib/modules\n";
84}
85
86# if no kernel version is contained in the basedir, try to find one
87if($basedir !~ m-/lib/modules/\d\.\d-) {
88    opendir(BD, $basedir) or die "can't open basedir $basedir : $!\n";
89    foreach ( readdir(BD) ) {
90        next if /^\.\.?$/;
91        next unless -d "$basedir/$_";
92        warn "dir = $_\n" if $verbose;
93        if( /^\d\.\d/ ) {
94            $kdir = $_;
95            warn("Guessed module directory as $basedir/$kdir\n");
96            last;
97        }
98    }
99    closedir(BD);
100    die "Cannot find a kernel version under $basedir\n" unless $kdir;
101    $basedir = "$basedir/$kdir";
102}
103
104# Find the list of .o or .ko files living under $basedir
105warn "**** Locating all modules\n" if $verbose;
106find sub {
107    my $file;
108	if ( -f $_  && ! -d $_ ) {
109		$file = $File::Find::name;
110		if ( $file =~ /\.k?o$/ ) {
111			push(@liblist, $file);
112			warn "$file\n" if $verbose;
113		}
114	}
115}, $basedir;
116warn "**** Finished locating modules\n" if $verbose;
117
118foreach my $obj ( @liblist ){
119    # turn the input file name into a target tag name
120    my ($tgtname) = $obj =~ m-(/lib/modules/.*)$-;
121
122    warn "\nMODULE = $tgtname\n" if $verbose;
123
124    # get a list of symbols
125	my @output=`$nm $obj`;
126
127    build_ref_tables($tgtname, \@output, $exp, $dep);
128}
129
130
131# vmlinux is a special name that is only used to resolve symbols
132my $tgtname = 'vmlinux';
133my @output = $kernelsyms ? `cat $kernelsyms` : `$nm $kernel`;
134warn "\nMODULE = $tgtname\n" if $verbose;
135build_ref_tables($tgtname, \@output, $exp, $dep);
136
137# resolve the dependencies for each module
138# reduce dependencies: remove unresolvable and resolved from vmlinux/System.map
139# remove duplicates
140foreach my $module (keys %$dep) {
141    warn "reducing module: $module\n" if $verbose;
142    $mod->{$module} = {};
143    foreach (@{$dep->{$module}}) {
144        if( $exp->{$_} ) {
145            warn "resolved symbol $_ in file $exp->{$_}\n" if $verbose;
146            next if $exp->{$_} =~ /vmlinux/;
147            $mod->{$module}{$exp->{$_}} = 1;
148        } else {
149            warn "unresolved symbol $_ in file $module\n";
150        }
151    }
152}
153
154# build a complete dependency list for each module and make sure it
155# is kept in order proper order
156my $mod2 = {};
157sub maybe_unshift
158{
159	my ($array, $ele) = @_;
160	# chop off the leading path /lib/modules/<kver>/ as modprobe
161	# will handle relative paths just fine
162##!!	$ele =~ s:^/lib/modules/[^/]*/::;
163	foreach (@{$array}) {
164		if ($_ eq $ele) {
165			return;
166		}
167	}
168	unshift (@{$array}, $ele);
169}
170sub add_mod_deps
171{
172	my ($depth, $mod, $mod2, $module, $this_module) = @_;
173
174	$depth .= " ";
175	warn "${depth}loading deps of module: $this_module\n" if $verbose;
176
177	foreach my $md (keys %{$mod->{$this_module}}) {
178		add_mod_deps ($depth, $mod, $mod2, $module, $md);
179		warn "${depth} outputting $md\n" if $verbose;
180		maybe_unshift (\@{$$mod2->{$module}}, $md);
181	}
182
183	if (!%{$mod->{$this_module}}) {
184		warn "${depth} no deps\n" if $verbose;
185	}
186}
187foreach my $module (keys %$mod) {
188	warn "filling out module: $module\n" if $verbose;
189	@{$mod2->{$module}} = ();
190	add_mod_deps ("", $mod, \$mod2, $module, $module);
191}
192
193# figure out where the output should go
194if ($stdout == 0) {
195	warn "writing $basedir/modules.dep\n" if $verbose;
196    open(STDOUT, ">$basedir/modules.dep")
197                             or die "cannot open $basedir/modules.dep: $!";
198}
199my $kseries = $basedir =~ m,/2\.6\.[^/]*, ? '2.6' : '2.4';
200
201foreach my $module ( keys %$mod ) {
202    if($kseries eq '2.4') {
203	    print "$module:\t";
204	    my @sorted = sort bydep keys %{$mod->{$module}};
205	    print join(" ",@sorted);
206	    print "\n\n";
207    } else {
208	    my $shortmod = $module;
209##!!	    $shortmod =~ s:^/lib/modules/[^/]*/::;
210	    print "$shortmod:";
211	    my @sorted = @{$mod2->{$module}};
212	    printf " " if @sorted;
213	    print join(" ",@sorted);
214	    print "\n";
215    }
216}
217
218
219sub build_ref_tables
220{
221    my ($name, $sym_ar, $exp, $dep) = @_;
222
223	my $ksymtab = grep m/ ${symprefix}__ksymtab/, @$sym_ar;
224
225    # gather the exported symbols
226	if($ksymtab){
227        # explicitly exported
228        foreach ( @$sym_ar ) {
229            / ${symprefix}__ksymtab_(.*)$/ and do {
230                my $sym = ${symprefix} . $1;
231                warn "sym = $sym\n" if $verbose;
232                $exp->{$sym} = $name;
233            };
234        }
235	} else {
236        # exporting all symbols
237        foreach ( @$sym_ar ) {
238            / [ABCDGRSTW] (.*)$/ and do {
239                warn "syma = $1\n" if $verbose;
240                $exp->{$1} = $name;
241            };
242        }
243	}
244
245    # this takes makes sure modules with no dependencies get listed
246    push @{$dep->{$name}}, $symprefix . 'printk' unless $name eq 'vmlinux';
247
248    # gather the unresolved symbols
249    foreach ( @$sym_ar ) {
250        !/ ${symprefix}__this_module/ && / U (.*)$/ and do {
251            warn "und = $1\n" if $verbose;
252            push @{$dep->{$name}}, $1;
253        };
254    }
255}
256
257sub bydep
258{
259    foreach my $f ( keys %{$mod->{$b}} ) {
260        if($f eq $a) {
261            return 1;
262        }
263    }
264    return -1;
265}
266
267
268
269__END__
270
271=head1 NAME
272
273depmod.pl - a cross platform script to generate kernel module
274dependency lists (modules.conf) which can then be used by modprobe
275on the target platform.
276
277It supports Linux 2.4 and 2.6 styles of modules.conf (auto-detected)
278
279=head1 SYNOPSIS
280
281depmod.pl [OPTION]... [basedir]...
282
283Example:
284
285	depmod.pl -F linux/System.map -b target/lib/modules/2.6.11
286
287=head1 DESCRIPTION
288
289The purpose of this script is to automagically generate a list of of kernel
290module dependencies.  This script produces dependency lists that should be
291identical to the depmod program from the modutils package.  Unlike the depmod
292binary, however, depmod.pl is designed to be run on your host system, not
293on your target system.
294
295This script was written by David Schleef <ds@schleef.org> to be used in
296conjunction with the BusyBox modprobe applet.
297
298=head1 OPTIONS
299
300=over 4
301
302=item B<-h --help>
303
304This displays the help message.
305
306=item B<-b --basedir>
307
308The base directory uner which the target's modules will be found.  This
309defaults to the /lib/modules directory.
310
311If you don't specify the kernel version, this script will search for
312one under the specified based directory and use the first thing that
313looks like a kernel version.
314
315=item B<-k --kernel>
316
317Kernel binary for the target (vmlinux).  You must either supply a kernel binary
318or a kernel symbol file (using the -F option).
319
320=item B<-F --kernelsyms>
321
322Kernel symbol file for the target (System.map).
323
324=item B<-n --stdout>
325
326Write to stdout instead of modules.dep
327kernel binary for the target (using the -k option).
328
329=item B<--verbose>
330
331Verbose (debug) output
332
333=back
334
335=head1 COPYRIGHT AND LICENSE
336
337 Copyright (c) 2001 David Schleef <ds@schleef.org>
338 Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
339 Copyright (c) 2001 Stuart Hughes <seh@zee2.com>
340 Copyright (c) 2002 Steven J. Hill <shill@broadcom.com>
341 Copyright (c) 2006 Freescale Semiconductor, Inc <stuarth@freescale.com>
342
343This program is free software; you can redistribute it and/or modify it
344under the same terms as Perl itself.
345
346=head1 AUTHOR
347
348David Schleef <ds@schleef.org>
349
350=cut
351