linux/scripts/namespace.pl
<<
>>
Prefs
   1#!/usr/bin/perl -w
   2#
   3#       namespace.pl.  Mon Aug 30 2004
   4#
   5#       Perform a name space analysis on the linux kernel.
   6#
   7#       Copyright Keith Owens <kaos@ocs.com.au>.  GPL.
   8#
   9#       Invoke by changing directory to the top of the kernel object
  10#       tree then namespace.pl, no parameters.
  11#
  12#       Tuned for 2.1.x kernels with the new module handling, it will
  13#       work with 2.0 kernels as well.
  14#
  15#       Last change 2.6.9-rc1, adding support for separate source and object
  16#       trees.
  17#
  18#       The source must be compiled/assembled first, the object files
  19#       are the primary input to this script.  Incomplete or missing
  20#       objects will result in a flawed analysis.  Compile both vmlinux
  21#       and modules.
  22#
  23#       Even with complete objects, treat the result of the analysis
  24#       with caution.  Some external references are only used by
  25#       certain architectures, others with certain combinations of
  26#       configuration parameters.  Ideally the source should include
  27#       something like
  28#
  29#       #ifndef CONFIG_...
  30#       static
  31#       #endif
  32#       symbol_definition;
  33#
  34#       so the symbols are defined as static unless a particular
  35#       CONFIG_... requires it to be external.
  36#
  37#       A symbol that is suffixed with '(export only)' has these properties
  38#
  39#       * It is global.
  40#       * It is marked EXPORT_SYMBOL or EXPORT_SYMBOL_GPL, either in the same
  41#         source file or a different source file.
  42#       * Given the current .config, nothing uses the symbol.
  43#
  44#       The symbol is a candidate for conversion to static, plus removal of the
  45#       export.  But be careful that a different .config might use the symbol.
  46#
  47#
  48#       Name space analysis and cleanup is an iterative process.  You cannot
  49#       expect to find all the problems in a single pass.
  50#
  51#       * Identify possibly unnecessary global declarations, verify that they
  52#         really are unnecessary and change them to static.
  53#       * Compile and fix up gcc warnings about static, removing dead symbols
  54#         as necessary.
  55#       * make clean and rebuild with different configs (especially
  56#         CONFIG_MODULES=n) to see which symbols are being defined when the
  57#         config does not require them.  These symbols bloat the kernel object
  58#         for no good reason, which is frustrating for embedded systems.
  59#       * Wrap config sensitive symbols in #ifdef CONFIG_foo, as long as the
  60#         code does not get too ugly.
  61#       * Repeat the name space analysis until you can live with with the
  62#         result.
  63#
  64
  65require 5;      # at least perl 5
  66use strict;
  67use File::Find;
  68
  69my $nm = ($ENV{'NM'} || "nm") . " -p";
  70my $objdump = ($ENV{'OBJDUMP'} || "objdump") . " -s -j .comment";
  71my $srctree = "";
  72my $objtree = "";
  73$srctree = "$ENV{'srctree'}/" if (exists($ENV{'srctree'}));
  74$objtree = "$ENV{'objtree'}/" if (exists($ENV{'objtree'}));
  75
  76if ($#ARGV != -1) {
  77        print STDERR "usage: $0 takes no parameters\n";
  78        die("giving up\n");
  79}
  80
  81my %nmdata = ();        # nm data for each object
  82my %def = ();           # all definitions for each name
  83my %ksymtab = ();       # names that appear in __ksymtab_
  84my %ref = ();           # $ref{$name} exists if there is a true external reference to $name
  85my %export = ();        # $export{$name} exists if there is an EXPORT_... of $name
  86
  87my %nmexception = (
  88    'fs/ext3/bitmap'                    => 1,
  89    'fs/ext4/bitmap'                    => 1,
  90    'arch/x86/lib/thunk_32'             => 1,
  91    'arch/x86/lib/cmpxchg'              => 1,
  92    'arch/x86/vdso/vdso32/note'         => 1,
  93    'lib/irq_regs'                      => 1,
  94    'usr/initramfs_data'                => 1,
  95    'drivers/scsi/aic94xx/aic94xx_dump' => 1,
  96    'drivers/scsi/libsas/sas_dump'      => 1,
  97    'lib/dec_and_lock'                  => 1,
  98    'drivers/ide/ide-probe-mini'        => 1,
  99    'usr/initramfs_data'                => 1,
 100    'drivers/acpi/acpia/exdump'         => 1,
 101    'drivers/acpi/acpia/rsdump'         => 1,
 102    'drivers/acpi/acpia/nsdumpdv'       => 1,
 103    'drivers/acpi/acpia/nsdump'         => 1,
 104    'arch/ia64/sn/kernel/sn2/io'        => 1,
 105    'arch/ia64/kernel/gate-data'        => 1,
 106    'security/capability'               => 1,
 107    'fs/ntfs/sysctl'                    => 1,
 108    'fs/jfs/jfs_debug'                  => 1,
 109);
 110
 111my %nameexception = (
 112    'mod_use_count_'     => 1,
 113    '__initramfs_end'   => 1,
 114    '__initramfs_start' => 1,
 115    '_einittext'        => 1,
 116    '_sinittext'        => 1,
 117    'kallsyms_names'    => 1,
 118    'kallsyms_num_syms' => 1,
 119    'kallsyms_addresses'=> 1,
 120    '__this_module'     => 1,
 121    '_etext'            => 1,
 122    '_edata'            => 1,
 123    '_end'              => 1,
 124    '__bss_start'       => 1,
 125    '_text'             => 1,
 126    '_stext'            => 1,
 127    '__gp'              => 1,
 128    'ia64_unw_start'    => 1,
 129    'ia64_unw_end'      => 1,
 130    '__init_begin'      => 1,
 131    '__init_end'        => 1,
 132    '__bss_stop'        => 1,
 133    '__nosave_begin'    => 1,
 134    '__nosave_end'      => 1,
 135    'pg0'               => 1,
 136    'vdso_enabled'      => 1,
 137    '__stack_chk_fail'  => 1,
 138    'VDSO32_PRELINK'    => 1,
 139    'VDSO32_vsyscall'   => 1,
 140    'VDSO32_rt_sigreturn'=>1,
 141    'VDSO32_sigreturn'  => 1,
 142);
 143
 144
 145&find(\&linux_objects, '.');    # find the objects and do_nm on them
 146&list_multiply_defined();
 147&resolve_external_references();
 148&list_extra_externals();
 149
 150exit(0);
 151
 152sub linux_objects
 153{
 154        # Select objects, ignoring objects which are only created by
 155        # merging other objects.  Also ignore all of modules, scripts
 156        # and compressed.  Most conglomerate objects are handled by do_nm,
 157        # this list only contains the special cases.  These include objects
 158        # that are linked from just one other object and objects for which
 159        # there is really no permanent source file.
 160        my $basename = $_;
 161        $_ = $File::Find::name;
 162        s:^\./::;
 163        if (/.*\.o$/ &&
 164                ! (
 165                m:/built-in.o$:
 166                || m:arch/x86/vdso/:
 167                || m:arch/x86/boot/:
 168                || m:arch/ia64/ia32/ia32.o$:
 169                || m:arch/ia64/kernel/gate-syms.o$:
 170                || m:arch/ia64/lib/__divdi3.o$:
 171                || m:arch/ia64/lib/__divsi3.o$:
 172                || m:arch/ia64/lib/__moddi3.o$:
 173                || m:arch/ia64/lib/__modsi3.o$:
 174                || m:arch/ia64/lib/__udivdi3.o$:
 175                || m:arch/ia64/lib/__udivsi3.o$:
 176                || m:arch/ia64/lib/__umoddi3.o$:
 177                || m:arch/ia64/lib/__umodsi3.o$:
 178                || m:arch/ia64/scripts/check_gas_for_hint.o$:
 179                || m:arch/ia64/sn/kernel/xp.o$:
 180                || m:boot/bbootsect.o$:
 181                || m:boot/bsetup.o$:
 182                || m:/bootsect.o$:
 183                || m:/boot/setup.o$:
 184                || m:/compressed/:
 185                || m:drivers/cdrom/driver.o$:
 186                || m:drivers/char/drm/tdfx_drv.o$:
 187                || m:drivers/ide/ide-detect.o$:
 188                || m:drivers/ide/pci/idedriver-pci.o$:
 189                || m:drivers/media/media.o$:
 190                || m:drivers/scsi/sd_mod.o$:
 191                || m:drivers/video/video.o$:
 192                || m:fs/devpts/devpts.o$:
 193                || m:fs/exportfs/exportfs.o$:
 194                || m:fs/hugetlbfs/hugetlbfs.o$:
 195                || m:fs/msdos/msdos.o$:
 196                || m:fs/nls/nls.o$:
 197                || m:fs/ramfs/ramfs.o$:
 198                || m:fs/romfs/romfs.o$:
 199                || m:fs/vfat/vfat.o$:
 200                || m:init/mounts.o$:
 201                || m:^modules/:
 202                || m:net/netlink/netlink.o$:
 203                || m:net/sched/sched.o$:
 204                || m:/piggy.o$:
 205                || m:^scripts/:
 206                || m:sound/.*/snd-:
 207                || m:^.*/\.tmp_:
 208                || m:^\.tmp_:
 209                || m:/vmlinux-obj.o$:
 210                || m:^tools/:
 211                )
 212        ) {
 213                do_nm($basename, $_);
 214        }
 215        $_ = $basename;         # File::Find expects $_ untouched (undocumented)
 216}
 217
 218sub do_nm
 219{
 220        my ($basename, $fullname) = @_;
 221        my ($source, $type, $name);
 222        if (! -e $basename) {
 223                printf STDERR "$basename does not exist\n";
 224                return;
 225        }
 226        if ($fullname !~ /\.o$/) {
 227                printf STDERR "$fullname is not an object file\n";
 228                return;
 229        }
 230        ($source = $basename) =~ s/\.o$//;
 231        if (-e "$source.c" || -e "$source.S") {
 232                $source = "$objtree$File::Find::dir/$source";
 233        } else {
 234                $source = "$srctree$File::Find::dir/$source";
 235        }
 236        if (! -e "$source.c" && ! -e "$source.S") {
 237                # No obvious source, exclude the object if it is conglomerate
 238                open(my $objdumpdata, "$objdump $basename|")
 239                    or die "$objdump $fullname failed $!\n";
 240
 241                my $comment;
 242                while (<$objdumpdata>) {
 243                        chomp();
 244                        if (/^In archive/) {
 245                                # Archives are always conglomerate
 246                                $comment = "GCC:GCC:";
 247                                last;
 248                        }
 249                        next if (! /^[ 0-9a-f]{5,} /);
 250                        $comment .= substr($_, 43);
 251                }
 252                close($objdumpdata);
 253
 254                if (!defined($comment) || $comment !~ /GCC\:.*GCC\:/m) {
 255                        printf STDERR "No source file found for $fullname\n";
 256                }
 257                return;
 258        }
 259        open (my $nmdata, "$nm $basename|")
 260            or die "$nm $fullname failed $!\n";
 261
 262        my @nmdata;
 263        while (<$nmdata>) {
 264                chop;
 265                ($type, $name) = (split(/ +/, $_, 3))[1..2];
 266                # Expected types
 267                # A absolute symbol
 268                # B weak external reference to data that has been resolved
 269                # C global variable, uninitialised
 270                # D global variable, initialised
 271                # G global variable, initialised, small data section
 272                # R global array, initialised
 273                # S global variable, uninitialised, small bss
 274                # T global label/procedure
 275                # U external reference
 276                # W weak external reference to text that has been resolved
 277                # V similar to W, but the value of the weak symbol becomes zero with no error.
 278                # a assembler equate
 279                # b static variable, uninitialised
 280                # d static variable, initialised
 281                # g static variable, initialised, small data section
 282                # r static array, initialised
 283                # s static variable, uninitialised, small bss
 284                # t static label/procedures
 285                # w weak external reference to text that has not been resolved
 286                # v similar to w
 287                # ? undefined type, used a lot by modules
 288                if ($type !~ /^[ABCDGRSTUWVabdgrstwv?]$/) {
 289                        printf STDERR "nm output for $fullname contains unknown type '$_'\n";
 290                }
 291                elsif ($name =~ /\./) {
 292                        # name with '.' is local static
 293                }
 294                else {
 295                        $type = 'R' if ($type eq '?');  # binutils replaced ? with R at one point
 296                        # binutils keeps changing the type for exported symbols, force it to R
 297                        $type = 'R' if ($name =~ /^__ksymtab/ || $name =~ /^__kstrtab/);
 298                        $name =~ s/_R[a-f0-9]{8}$//;    # module versions adds this
 299                        if ($type =~ /[ABCDGRSTWV]/ &&
 300                                $name ne 'init_module' &&
 301                                $name ne 'cleanup_module' &&
 302                                $name ne 'Using_Versions' &&
 303                                $name !~ /^Version_[0-9]+$/ &&
 304                                $name !~ /^__parm_/ &&
 305                                $name !~ /^__kstrtab/ &&
 306                                $name !~ /^__ksymtab/ &&
 307                                $name !~ /^__kcrctab_/ &&
 308                                $name !~ /^__exitcall_/ &&
 309                                $name !~ /^__initcall_/ &&
 310                                $name !~ /^__kdb_initcall_/ &&
 311                                $name !~ /^__kdb_exitcall_/ &&
 312                                $name !~ /^__module_/ &&
 313                                $name !~ /^__mod_/ &&
 314                                $name !~ /^__crc_/ &&
 315                                $name ne '__this_module' &&
 316                                $name ne 'kernel_version') {
 317                                if (!exists($def{$name})) {
 318                                        $def{$name} = [];
 319                                }
 320                                push(@{$def{$name}}, $fullname);
 321                        }
 322                        push(@nmdata, "$type $name");
 323                        if ($name =~ /^__ksymtab_/) {
 324                                $name = substr($name, 10);
 325                                if (!exists($ksymtab{$name})) {
 326                                        $ksymtab{$name} = [];
 327                                }
 328                                push(@{$ksymtab{$name}}, $fullname);
 329                        }
 330                }
 331        }
 332        close($nmdata);
 333
 334        if ($#nmdata < 0) {
 335            printf "No nm data for $fullname\n"
 336                unless $nmexception{$fullname};
 337            return;
 338        }
 339        $nmdata{$fullname} = \@nmdata;
 340}
 341
 342sub drop_def
 343{
 344        my ($object, $name) = @_;
 345        my $nmdata = $nmdata{$object};
 346        my ($i, $j);
 347        for ($i = 0; $i <= $#{$nmdata}; ++$i) {
 348                if ($name eq (split(' ', $nmdata->[$i], 2))[1]) {
 349                        splice(@{$nmdata{$object}}, $i, 1);
 350                        my $def = $def{$name};
 351                        for ($j = 0; $j < $#{$def{$name}}; ++$j) {
 352                                if ($def{$name}[$j] eq $object) {
 353                                        splice(@{$def{$name}}, $j, 1);
 354                                }
 355                        }
 356                        last;
 357                }
 358        }
 359}
 360
 361sub list_multiply_defined
 362{
 363        foreach my $name (keys(%def)) {
 364                if ($#{$def{$name}} > 0) {
 365                        # Special case for cond_syscall
 366                        if ($#{$def{$name}} == 1 &&
 367                           ($name =~ /^sys_/ || $name =~ /^compat_sys_/ ||
 368                            $name =~ /^sys32_/)) {
 369                                if($def{$name}[0] eq "kernel/sys_ni.o" ||
 370                                   $def{$name}[1] eq "kernel/sys_ni.o") {
 371                                        &drop_def("kernel/sys_ni.o", $name);
 372                                        next;
 373                                }
 374                        }
 375
 376                        printf "$name is multiply defined in :-\n";
 377                        foreach my $module (@{$def{$name}}) {
 378                                printf "\t$module\n";
 379                        }
 380                }
 381        }
 382}
 383
 384sub resolve_external_references
 385{
 386        my ($kstrtab, $ksymtab, $export);
 387
 388        printf "\n";
 389        foreach my $object (keys(%nmdata)) {
 390                my $nmdata = $nmdata{$object};
 391                for (my $i = 0; $i <= $#{$nmdata}; ++$i) {
 392                        my ($type, $name) = split(' ', $nmdata->[$i], 2);
 393                        if ($type eq "U" || $type eq "w") {
 394                                if (exists($def{$name}) || exists($ksymtab{$name})) {
 395                                        # add the owning object to the nmdata
 396                                        $nmdata->[$i] = "$type $name $object";
 397                                        # only count as a reference if it is not EXPORT_...
 398                                        $kstrtab = "R __kstrtab_$name";
 399                                        $ksymtab = "R __ksymtab_$name";
 400                                        $export = 0;
 401                                        for (my $j = 0; $j <= $#{$nmdata}; ++$j) {
 402                                                if ($nmdata->[$j] eq $kstrtab ||
 403                                                    $nmdata->[$j] eq $ksymtab) {
 404                                                        $export = 1;
 405                                                        last;
 406                                                }
 407                                        }
 408                                        if ($export) {
 409                                                $export{$name} = "";
 410                                        }
 411                                        else {
 412                                                $ref{$name} = ""
 413                                        }
 414                                }
 415                                elsif ( ! $nameexception{$name}
 416                                        && $name !~ /^__sched_text_/
 417                                        && $name !~ /^__start_/
 418                                        && $name !~ /^__end_/
 419                                        && $name !~ /^__stop_/
 420                                        && $name !~ /^__scheduling_functions_.*_here/
 421                                        && $name !~ /^__.*initcall_/
 422                                        && $name !~ /^__.*per_cpu_start/
 423                                        && $name !~ /^__.*per_cpu_end/
 424                                        && $name !~ /^__alt_instructions/
 425                                        && $name !~ /^__setup_/
 426                                        && $name !~ /^__mod_timer/
 427                                        && $name !~ /^__mod_page_state/
 428                                        && $name !~ /^init_module/
 429                                        && $name !~ /^cleanup_module/
 430                                ) {
 431                                        printf "Cannot resolve ";
 432                                        printf "weak " if ($type eq "w");
 433                                        printf "reference to $name from $object\n";
 434                                }
 435                        }
 436                }
 437        }
 438}
 439
 440sub list_extra_externals
 441{
 442        my %noref = ();
 443
 444        foreach my $name (keys(%def)) {
 445                if (! exists($ref{$name})) {
 446                        my @module = @{$def{$name}};
 447                        foreach my $module (@module) {
 448                                if (! exists($noref{$module})) {
 449                                        $noref{$module} = [];
 450                                }
 451                                push(@{$noref{$module}}, $name);
 452                        }
 453                }
 454        }
 455        if (%noref) {
 456                printf "\nExternally defined symbols with no external references\n";
 457                foreach my $module (sort(keys(%noref))) {
 458                        printf "  $module\n";
 459                        foreach (sort(@{$noref{$module}})) {
 460                            my $export;
 461                            if (exists($export{$_})) {
 462                                $export = " (export only)";
 463                            } else {
 464                                $export = "";
 465                            }
 466                            printf "    $_$export\n";
 467                        }
 468                }
 469        }
 470}
 471