diff options
Diffstat (limited to 'final/runtime/tools/check-depends.pl')
-rwxr-xr-x | final/runtime/tools/check-depends.pl | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/final/runtime/tools/check-depends.pl b/final/runtime/tools/check-depends.pl new file mode 100755 index 0000000..47e7e5a --- /dev/null +++ b/final/runtime/tools/check-depends.pl @@ -0,0 +1,506 @@ +#!/usr/bin/env perl + +# +#//===----------------------------------------------------------------------===// +#// +#// The LLVM Compiler Infrastructure +#// +#// This file is dual licensed under the MIT and the University of Illinois Open +#// Source Licenses. See LICENSE.txt for details. +#// +#//===----------------------------------------------------------------------===// +# + +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin/lib"; + +use tools; + +our $VERSION = "0.005"; +my $target_os; +my $target_arch; + +# -------------------------------------------------------------------------------------------------- +# Ouput parse error. +# $tool -- Name of tool. +# @bulk -- Output of the tool. +# $n -- Number of line caused parse error. +sub parse_error($\@$) { + my ( $tool, $bulk, $n ) = @_; + my @bulk; + for ( my $i = 0; $i < @$bulk; ++ $i ) { + push( @bulk, ( $i == $n ? ">>> " : " " ) . $bulk->[ $i ] ); + }; # for $i + runtime_error( "Fail to parse $tool output:", @bulk, "(eof)" ); +}; # sub parse_error + + +# -------------------------------------------------------------------------------------------------- +# Linux* OS version of get_deps() parses output of ldd: +# +# $ ldd libname.so +# libc.so.6 => /lib64/libc.so.6 (0x00002b60fedd8000) +# libdl.so.2 => /lib64/libdl.so.2 (0x00002b60ff12b000) +# libpthread.so.0 => /lib64/libpthread.so.0 (0x00002b60ff32f000) +# /lib64/ld-linux-x86-64.so.2 (0x0000003879400000) +# +# Note: ldd printd all the dependencies, direct and indirect. (For example, if specified library +# requires libdl.so, and libdl.so requires /lib/ld-linux.so, ldd prints both libdl.so and +# /lib/ld-linux.so). If you do not want indirect dependencies, look at readelf tool. +# +sub get_deps_ldd($) { + + my $lib = shift ( @_ ); + my $tool = "ldd"; + my @bulk; + my @deps; + + execute( [ $tool, $lib ], -stdout => \@bulk ); + debug( @bulk, "(eof)" ); + + foreach my $i ( 0 .. @bulk - 1 ) { + my $line = $bulk[ $i ]; + if ( $line !~ m{^\s*(?:([_a-z0-9.+-/]*)\s+=>\s+)?([_a-z0-9.+-/]*)\s+\(0x[0-9a-z]*\)$}i ) { + parse_error( $tool, @bulk, $i ); + }; # if + my $dep = ( defined( $1 ) ? $1 : $2 ); + push( @deps, $dep ); + }; # foreach $i + + return @deps; + +}; # sub get_deps_ldd + + +# -------------------------------------------------------------------------------------------------- +# Another Linux* OS version of get_deps() parses output of readelf: +# +# $ readelf -d exports/lin_32e/lib/libomp.so +# +# Dynamic segment at offset 0x87008 contains 24 entries: +# Tag Type Name/Value +# 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] +# 0x0000000000000001 (NEEDED) Shared library: [libdl.so.2] +# 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0] +# 0x000000000000000e (SONAME) Library soname: [libomp.so] +# 0x000000000000000d (FINI) 0x51caa +# 0x0000000000000004 (HASH) 0x158 +# 0x0000000000000005 (STRTAB) 0x9350 +# ... +# +# Note: In contrast to ldd, readlef shows only direct dependencies. +# +sub get_deps_readelf($) { + + my $file = shift ( @_ ); + my $tool; + my @bulk; + my @deps; + + if($target_arch eq "mic") { + $tool = "x86_64-k1om-linux-readelf"; + } else { + $tool = "readelf"; + } + + # Force the readelf call to be in English. For example, when readelf + # is called on a french localization, it will find "Librairie partagees" + # instead of shared library + $ENV{ LANG } = "C"; + + execute( [ $tool, "-d", $file ], -stdout => \@bulk ); + debug( @bulk, "(eof)" ); + + my $i = 0; + # Parse header. + ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) + or parse_error( $tool, @bulk, $i ); + ++ $i; + if ( $i == @bulk - 1 and $bulk[ $i ] =~ m{^There is no dynamic section in this file\.\s*$} ) { + # This is not dynamic executable => no dependencies. + return @deps; + }; # if + ( $i < @bulk and $bulk[ $i ] =~ m{^Dynamic (?:segment|section) at offset 0x[0-9a-f]+ contains \d+ entries:\s*$} ) + or parse_error( $tool, @bulk, $i ); + ++ $i; + ( $i < @bulk and $bulk[ $i ] =~ m{^\s*Tag\s+Type\s+Name/Value\s*$} ) + or parse_error( $tool, @bulk, $i ); + ++ $i; + # Parse body. + while ( $i < @bulk ) { + my $line = $bulk[ $i ]; + if ( $line !~ m{^\s*0x[0-9a-f]+\s+\(([_A-Z0-9]+)\)\s+(.*)\s*$}i ) { + parse_error( $tool, @bulk, $i ); + }; # if + my ( $type, $value ) = ( $1, $2 ); + if ( $type eq "NEEDED" ) { + if ( $value !~ m{\AShared library: \[(.*)\]\z} ) { + parse_error( $tool, @bulk, $i ); + }; # if + my $dep = $1; + push( @deps, $dep ); + }; # if + ++ $i; + }; # foreach $i + + return @deps; + +}; # sub get_deps_readelf + + +# -------------------------------------------------------------------------------------------------- +# OS X* version of get_deps() parses output of otool: +# +# $ otool -L libname.dylib +# exports/mac_32/lib.thin/libomp.dylib: +# libomp.dylib (compatibility version 5.0.0, current version 5.0.0) +# /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.1.3) +# +sub get_deps_otool($) { + + my $file = shift ( @_ ); + my $name = get_file( $file ); + my $tool = "otool"; + my @bulk; + my @deps; + + if ( $target_arch eq "32e" ) { + # On older (Tiger) systems otool does not recognize 64-bit binaries, so try to locate + # otool64. + my $path = which( "otool64" ); + if ( defined ( $path ) ) { + $tool = "otool64"; + }; # if + }; # if + + execute( [ $tool, "-L", $file ], -stdout => \@bulk ); + debug( @bulk, "(eof)" ); + + my $i = 0; + # Parse the first one or two lines separately. + ( $i < @bulk and $bulk[ $i ] =~ m{^\Q$file\E:$} ) + or parse_error( $tool, @bulk, $i ); + ++ $i; + if ( $name =~ m{\.dylib\z} ) { + # Added "@rpath/" enables dynamic load of the library designated at link time. + $name = '@rpath/' . $name; + # In case of dynamic library otool print the library itself as a dependent library. + ( $i < @bulk and $bulk[ $i ] =~ m{^\s+\Q$name\E\s+\(compatibility version.*\)$} ) + or parse_error( $tool, @bulk, $i ); + ++ $i; + }; # if + + # Then parse the rest. + while ( $i < @bulk ) { + my $line = $bulk[ $i ]; + if ( $line !~ m/^\s*(.*)\s+\(compatibility version\s.*\)$/ ) { + parse_error( $tool, @bulk, $i ); + }; # if + my ( $dep ) = ( $1 ); + push( @deps, $dep ); + ++ $i; + }; # while + + return @deps; + +}; # sub get_deps_otool + + +# -------------------------------------------------------------------------------------------------- +# Windows* OS version of get_deps() parses output of link: +# +# > link -dump -dependents libname.dll +# Microsoft (R) COFF/PE Dumper Version 8.00.40310.39 +# Copyright (C) Microsoft Corporation. All rights reserved. +# Dump of file S:\Projects.OMP\users\omalyshe\omp\libomp\exports\win_64\lib\libompmd.dll +# File Type: DLL +# Image has the following dependencies: +# KERNEL32.dll +# Summary +# C000 .data +# 6000 .pdata +# 18000 .rdata +# ... +# +# > link -dump -directives libname.lib +# Microsoft (R) COFF/PE Dumper Version 8.00.40310.39 +# Copyright (C) Microsoft Corporation. All rights reserved. +# Dump of file S:\Projects.OMP\users\omalyshe\omp\libomp\exports\win_32e\lib\libimp5mt.lib +# File Type: LIBRARY +# Linker Directives +# ----------------- +# -defaultlib:"uuid.lib" +# -defaultlib:"uuid.lib" +# ..... +# Summary +# 3250 .bss +# 3FBC .data +# 34 .data1 +# .... +sub get_deps_link($) { + + my ( $lib ) = @_; + my $tool = "link"; + my @bulk; + my @deps; + + my $ext = lc( get_ext( $lib ) ); + if ( $ext !~ m{\A\.(?:lib|dll|exe)\z}i ) { + runtime_error( "Incorrect file is specified: `$lib'; only `lib', `dll' or `exe' file expected" ); + }; # if + + execute( + [ $tool, "/dump", ( $ext eq ".lib" ? "/directives" : "/dependents" ), $lib ], + -stdout => \@bulk + ); + + debug( @bulk, "(eof)" ); + + my $i = 0; + ( $i < @bulk and $bulk[ $i ] =~ m{^Microsoft \(R\) COFF\/PE Dumper Version.*$} ) or parse_error( $tool, @bulk, $i ); ++ $i; + ( $i < @bulk and $bulk[ $i ] =~ m{^Copyright \(C\) Microsoft Corporation\..*$} ) or parse_error( $tool, @bulk, $i ); ++ $i; + ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i; + ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i; + ( $i < @bulk and $bulk[ $i ] =~ m{^Dump of file\s\Q$lib\E$} ) or parse_error( $tool, @bulk, $i ); ++ $i; + ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i; + ( $i < @bulk and $bulk[ $i ] =~ m{^File Type:\s(.*)$} ) or parse_error( $tool, @bulk, $i ); ++ $i; + ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i; + + if ( $ext eq ".lib" ) { + + my %deps; + while ( $i < @bulk ) { + my $line = $bulk[ $i ]; + if ( 0 ) { + } elsif ( $line =~ m{^\s*[-/]defaultlib\:(.*)\s*$}i ) { + my $dep = $1; + # Normalize library name: + $dep = lc( $1 ); # Convert to lower case. + $dep =~ s{\A"(.*)"\z}{$1}; # Drop surrounding quotes (if any). + $dep =~ s{\.lib\z}{}; # Drop .lib suffix (if any). + $deps{ $dep } = 1; + } elsif ( $line =~ m{^\s*Linker Directives\s*$} ) { + } elsif ( $line =~ m{^\s*-+\s*$} ) { + } elsif ( $line =~ m{^\s*/alternatename\:.*$} ) { + } elsif ( $line =~ m{^\s*$} ) { + } elsif ( $line =~ m{^\s*/FAILIFMISMATCH\:.*$} ) { + # This directive is produced only by _MSC_VER=1600 + } elsif ( $line =~ m{^\s*Summary\s*$} ) { + last; + } else { + parse_error( $tool, @bulk, $i ); + }; # if + ++ $i; + } # while + @deps = keys( %deps ); + + } else { + + ( $i < @bulk and $bulk[ $i ] =~ m{\s*Image has the following dependencies\:$} ) + or parse_error( $tool, @bulk, $i ); + ++ $i; + while ( $i < @bulk ) { + my $line = $bulk[ $i ]; + if ( 0 ) { + } elsif ( $line =~ m{^\s*$} ) { + # Ignore empty lines. + } elsif ( $line =~ m{^\s*(.*\.dll)$}i ) { + my $dep = lc( $1 ); + push( @deps, $dep ); + } elsif ( $line =~ m{^\s*Summary$} ) { + last; + } else { + parse_error( $tool, @bulk, $i ); + }; # if + ++ $i; + }; # while + + }; # if + + return @deps; + +}; # sub get_deps_link + + +# -------------------------------------------------------------------------------------------------- +# Main. +# -------------------------------------------------------------------------------------------------- + +# Parse command line. +my $expected; +my $bare; +Getopt::Long::Configure( "permute" ); +get_options( + "os=s" => \$target_os, + "arch=s" => \$target_arch, + "bare" => \$bare, + "expected=s" => \$expected, +); +my @expected; +if ( defined( $expected ) ) { + if ( $expected ne "none" ) { + @expected = sort( split( ",", $expected ) ); + if ( $target_os eq "win" ) { + @expected = map( lc( $_ ), @expected ); + }; # if + }; # if +}; # if +if ( @ARGV < 1 ) { + cmdline_error( "Specify a library name to check for dependencies" ); +}; # if +if ( @ARGV > 1 ) { + cmdline_error( "Too many arguments" ); +}; # if +my $lib = shift( @ARGV ); +if ( not -e $lib ){ + runtime_error( "Specified file does not exist: \"$lib\"" ); +}; # if + +# Select appropriate get_deps implementation. +if ( 0 ) { +} elsif ( $target_os eq "lin" ) { + *get_deps = \*get_deps_readelf; +} elsif ( $target_os eq "mac" ) { + *get_deps = \*get_deps_otool; +} elsif ( $target_os eq "win" ) { + *get_deps = \*get_deps_link; +} else { + runtime_error( "OS \"$target_os\" not supported" ); +}; # if + +# Do the work. +my @deps = sort( get_deps( $lib ) ); +if ( $bare ) { + print( map( "$_\n", @deps ) ); +} else { + info( "Dependencies:", @deps ? map( " $_", @deps ) : "(none)" ); +}; # if +if ( defined( $expected ) ) { + my %deps = map( ( $_ => 1 ), @deps ); + foreach my $dep ( @expected ) { + delete( $deps{ $dep } ); + }; # foreach + my @unexpected = sort( keys( %deps ) ); + if ( @unexpected ) { + runtime_error( "Unexpected dependencies:", map( " $_", @unexpected ) ); + }; # if +}; # if + +exit( 0 ); + +__END__ + +=pod + +=head1 NAME + +B<check-depends.pl> -- Check dependencies for a specified library. + +=head1 SYNOPSIS + +B<check-depends.pl> I<OPTIONS>... I<library> + +=head1 DESCRIPTION + +C<check-depends.pl> finds direct dependencies for a specified library. List of actual dependencies +is sorted alphabetically and printed. If list of expected dependencies is specified, the scripts +checks the library has only allowed dependencies. In case of not expected depndencies the script +issues error message and exits with non-zero code. + +Linux* OS and OS X*: The script finds dependencies only for dymamic libraries. Windows* OS: The script +finds dependencies for either static or dymamic libraries. + +The script uses external tools. On Linux* OS, it runs F<readelf>, on OS X* -- F<otool> (or F<otool64>), +on Windows* OS -- F<link>. + +On Windows* OS dependencies are printed in lower case, case of expected dependencies ignored. + +=head1 OPTIONS + +=over + +=item B<--bare> + +Do not use fancy formatting; produce plain, bare output: just a list of libraries, +a library per line. + +=item B<--expected=>I<list> + +I<list> is comma-separated list of expected dependencies (or C<none>). +If C<--expected> option specified, C<check-depends.pl> checks the specified library +has only expected dependencies. + +=item B<--os=>I<str> + +Specify target OS (tool to use) manually. +Useful for cross-build, when host OS is not the same as target OS. +I<str> should be either C<lin>, C<mac>, or C<win>. + +=back + +=head2 Standard Options + +=over + +=item B<--help> + +Print short help message and exit. + +=item B<--doc> + +=item B<--manual> + +Print full documentation and exit. + +=item B<--quiet> + +Do not output informational messages. + +=item B<--version> + +Print version and exit. + +=back + +=head1 ARGUMENTS + +=over + +=item I<library> + +A name of library to find or check dependencies. + +=back + +=head1 EXAMPLES + +Just print library dependencies (Windows* OS): + + > check-depends.pl exports/win_32/lib/libompmd.dll + check-depends.pl: (i) Dependencies: + check-depends.pl: (i) kernel32.dll + +Print library dependencies, use bare output (Linux* OS): + + $ check-depends.pl --bare exports/lin_32e/lib/libomp_db.so + libc.so.6 + libdl.so.2 + libpthread.so.0 + +Check the library does not have any dependencies (OS X*): + + $ check-depends.pl --expected=none exports/mac_32/lib/libomp.dylib + check-depends.pl: (i) Dependencies: + check-depends.pl: (i) /usr/lib/libSystem.B.dylib + check-depends.pl: (x) Unexpected dependencies: + check-depends.pl: (x) /usr/lib/libSystem.B.dylib + $ echo $? + 2 + +=cut + +# end of file # + |