You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			507 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			507 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
| #!/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 #
 | |
| 
 |