blob: 47e7e5a3d356ca2cdddd3f0f36d83ea8cf5042d6 [file] [log] [blame]
Jim Cownie5e8470a2013-09-27 10:38:44 +00001#!/usr/bin/env perl
2
3#
4#//===----------------------------------------------------------------------===//
5#//
6#// The LLVM Compiler Infrastructure
7#//
8#// This file is dual licensed under the MIT and the University of Illinois Open
9#// Source Licenses. See LICENSE.txt for details.
10#//
11#//===----------------------------------------------------------------------===//
12#
13
14use strict;
15use warnings;
16
17use FindBin;
18use lib "$FindBin::Bin/lib";
19
20use tools;
Jim Cownie5e8470a2013-09-27 10:38:44 +000021
22our $VERSION = "0.005";
Jonathan Peyton4c91ad12016-01-26 19:44:31 +000023my $target_os;
24my $target_arch;
Jim Cownie5e8470a2013-09-27 10:38:44 +000025
26# --------------------------------------------------------------------------------------------------
27# Ouput parse error.
28# $tool -- Name of tool.
29# @bulk -- Output of the tool.
30# $n -- Number of line caused parse error.
31sub parse_error($\@$) {
32 my ( $tool, $bulk, $n ) = @_;
33 my @bulk;
34 for ( my $i = 0; $i < @$bulk; ++ $i ) {
35 push( @bulk, ( $i == $n ? ">>> " : " " ) . $bulk->[ $i ] );
36 }; # for $i
37 runtime_error( "Fail to parse $tool output:", @bulk, "(eof)" );
38}; # sub parse_error
39
40
41# --------------------------------------------------------------------------------------------------
42# Linux* OS version of get_deps() parses output of ldd:
43#
44# $ ldd libname.so
45# libc.so.6 => /lib64/libc.so.6 (0x00002b60fedd8000)
46# libdl.so.2 => /lib64/libdl.so.2 (0x00002b60ff12b000)
47# libpthread.so.0 => /lib64/libpthread.so.0 (0x00002b60ff32f000)
48# /lib64/ld-linux-x86-64.so.2 (0x0000003879400000)
49#
50# Note: ldd printd all the dependencies, direct and indirect. (For example, if specified library
51# requires libdl.so, and libdl.so requires /lib/ld-linux.so, ldd prints both libdl.so and
52# /lib/ld-linux.so). If you do not want indirect dependencies, look at readelf tool.
53#
54sub get_deps_ldd($) {
55
56 my $lib = shift ( @_ );
57 my $tool = "ldd";
58 my @bulk;
59 my @deps;
60
61 execute( [ $tool, $lib ], -stdout => \@bulk );
62 debug( @bulk, "(eof)" );
63
64 foreach my $i ( 0 .. @bulk - 1 ) {
65 my $line = $bulk[ $i ];
66 if ( $line !~ m{^\s*(?:([_a-z0-9.+-/]*)\s+=>\s+)?([_a-z0-9.+-/]*)\s+\(0x[0-9a-z]*\)$}i ) {
67 parse_error( $tool, @bulk, $i );
68 }; # if
69 my $dep = ( defined( $1 ) ? $1 : $2 );
70 push( @deps, $dep );
71 }; # foreach $i
72
73 return @deps;
74
75}; # sub get_deps_ldd
76
77
78# --------------------------------------------------------------------------------------------------
79# Another Linux* OS version of get_deps() parses output of readelf:
80#
Jonathan Peytonf0efbb52015-06-01 02:41:44 +000081# $ readelf -d exports/lin_32e/lib/libomp.so
Jim Cownie5e8470a2013-09-27 10:38:44 +000082#
83# Dynamic segment at offset 0x87008 contains 24 entries:
84# Tag Type Name/Value
85# 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
86# 0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
87# 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
Jonathan Peytonf0efbb52015-06-01 02:41:44 +000088# 0x000000000000000e (SONAME) Library soname: [libomp.so]
Jim Cownie5e8470a2013-09-27 10:38:44 +000089# 0x000000000000000d (FINI) 0x51caa
90# 0x0000000000000004 (HASH) 0x158
91# 0x0000000000000005 (STRTAB) 0x9350
92# ...
93#
94# Note: In contrast to ldd, readlef shows only direct dependencies.
95#
96sub get_deps_readelf($) {
97
98 my $file = shift ( @_ );
Andrey Churbanovd315cea2015-01-16 12:54:51 +000099 my $tool;
Jim Cownie5e8470a2013-09-27 10:38:44 +0000100 my @bulk;
101 my @deps;
102
Andrey Churbanovd315cea2015-01-16 12:54:51 +0000103 if($target_arch eq "mic") {
104 $tool = "x86_64-k1om-linux-readelf";
105 } else {
106 $tool = "readelf";
107 }
108
Sylvestre Ledru6dabac82015-09-16 12:01:14 +0000109 # Force the readelf call to be in English. For example, when readelf
110 # is called on a french localization, it will find "Librairie partagees"
111 # instead of shared library
112 $ENV{ LANG } = "C";
113
Jim Cownie5e8470a2013-09-27 10:38:44 +0000114 execute( [ $tool, "-d", $file ], -stdout => \@bulk );
115 debug( @bulk, "(eof)" );
116
117 my $i = 0;
118 # Parse header.
119 ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} )
120 or parse_error( $tool, @bulk, $i );
121 ++ $i;
122 if ( $i == @bulk - 1 and $bulk[ $i ] =~ m{^There is no dynamic section in this file\.\s*$} ) {
123 # This is not dynamic executable => no dependencies.
124 return @deps;
125 }; # if
126 ( $i < @bulk and $bulk[ $i ] =~ m{^Dynamic (?:segment|section) at offset 0x[0-9a-f]+ contains \d+ entries:\s*$} )
127 or parse_error( $tool, @bulk, $i );
128 ++ $i;
129 ( $i < @bulk and $bulk[ $i ] =~ m{^\s*Tag\s+Type\s+Name/Value\s*$} )
130 or parse_error( $tool, @bulk, $i );
131 ++ $i;
132 # Parse body.
133 while ( $i < @bulk ) {
134 my $line = $bulk[ $i ];
135 if ( $line !~ m{^\s*0x[0-9a-f]+\s+\(([_A-Z0-9]+)\)\s+(.*)\s*$}i ) {
136 parse_error( $tool, @bulk, $i );
137 }; # if
138 my ( $type, $value ) = ( $1, $2 );
139 if ( $type eq "NEEDED" ) {
140 if ( $value !~ m{\AShared library: \[(.*)\]\z} ) {
141 parse_error( $tool, @bulk, $i );
142 }; # if
143 my $dep = $1;
144 push( @deps, $dep );
145 }; # if
146 ++ $i;
147 }; # foreach $i
148
149 return @deps;
150
151}; # sub get_deps_readelf
152
153
154# --------------------------------------------------------------------------------------------------
155# OS X* version of get_deps() parses output of otool:
156#
157# $ otool -L libname.dylib
Jonathan Peytonf0efbb52015-06-01 02:41:44 +0000158# exports/mac_32/lib.thin/libomp.dylib:
159# libomp.dylib (compatibility version 5.0.0, current version 5.0.0)
Jim Cownie5e8470a2013-09-27 10:38:44 +0000160# /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.1.3)
161#
162sub get_deps_otool($) {
163
164 my $file = shift ( @_ );
165 my $name = get_file( $file );
166 my $tool = "otool";
167 my @bulk;
168 my @deps;
169
170 if ( $target_arch eq "32e" ) {
171 # On older (Tiger) systems otool does not recognize 64-bit binaries, so try to locate
172 # otool64.
173 my $path = which( "otool64" );
174 if ( defined ( $path ) ) {
175 $tool = "otool64";
176 }; # if
177 }; # if
178
179 execute( [ $tool, "-L", $file ], -stdout => \@bulk );
180 debug( @bulk, "(eof)" );
181
182 my $i = 0;
183 # Parse the first one or two lines separately.
184 ( $i < @bulk and $bulk[ $i ] =~ m{^\Q$file\E:$} )
185 or parse_error( $tool, @bulk, $i );
186 ++ $i;
187 if ( $name =~ m{\.dylib\z} ) {
Jonathan Peyton89d9b332016-02-09 22:15:30 +0000188 # Added "@rpath/" enables dynamic load of the library designated at link time.
189 $name = '@rpath/' . $name;
Jim Cownie5e8470a2013-09-27 10:38:44 +0000190 # In case of dynamic library otool print the library itself as a dependent library.
191 ( $i < @bulk and $bulk[ $i ] =~ m{^\s+\Q$name\E\s+\(compatibility version.*\)$} )
192 or parse_error( $tool, @bulk, $i );
193 ++ $i;
194 }; # if
195
196 # Then parse the rest.
197 while ( $i < @bulk ) {
198 my $line = $bulk[ $i ];
199 if ( $line !~ m/^\s*(.*)\s+\(compatibility version\s.*\)$/ ) {
200 parse_error( $tool, @bulk, $i );
201 }; # if
202 my ( $dep ) = ( $1 );
203 push( @deps, $dep );
204 ++ $i;
205 }; # while
206
207 return @deps;
208
209}; # sub get_deps_otool
210
211
212# --------------------------------------------------------------------------------------------------
213# Windows* OS version of get_deps() parses output of link:
214#
215# > link -dump -dependents libname.dll
216# Microsoft (R) COFF/PE Dumper Version 8.00.40310.39
217# Copyright (C) Microsoft Corporation. All rights reserved.
Jonathan Peytonf0efbb52015-06-01 02:41:44 +0000218# Dump of file S:\Projects.OMP\users\omalyshe\omp\libomp\exports\win_64\lib\libompmd.dll
Jim Cownie5e8470a2013-09-27 10:38:44 +0000219# File Type: DLL
220# Image has the following dependencies:
221# KERNEL32.dll
222# Summary
223# C000 .data
224# 6000 .pdata
225# 18000 .rdata
226# ...
227#
228# > link -dump -directives libname.lib
229# Microsoft (R) COFF/PE Dumper Version 8.00.40310.39
230# Copyright (C) Microsoft Corporation. All rights reserved.
231# Dump of file S:\Projects.OMP\users\omalyshe\omp\libomp\exports\win_32e\lib\libimp5mt.lib
232# File Type: LIBRARY
233# Linker Directives
234# -----------------
235# -defaultlib:"uuid.lib"
236# -defaultlib:"uuid.lib"
237# .....
238# Summary
239# 3250 .bss
240# 3FBC .data
241# 34 .data1
242# ....
243sub get_deps_link($) {
244
245 my ( $lib ) = @_;
246 my $tool = "link";
247 my @bulk;
248 my @deps;
249
250 my $ext = lc( get_ext( $lib ) );
251 if ( $ext !~ m{\A\.(?:lib|dll|exe)\z}i ) {
252 runtime_error( "Incorrect file is specified: `$lib'; only `lib', `dll' or `exe' file expected" );
253 }; # if
254
255 execute(
256 [ $tool, "/dump", ( $ext eq ".lib" ? "/directives" : "/dependents" ), $lib ],
257 -stdout => \@bulk
258 );
259
260 debug( @bulk, "(eof)" );
261
262 my $i = 0;
263 ( $i < @bulk and $bulk[ $i ] =~ m{^Microsoft \(R\) COFF\/PE Dumper Version.*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
264 ( $i < @bulk and $bulk[ $i ] =~ m{^Copyright \(C\) Microsoft Corporation\..*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
265 ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
266 ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
267 ( $i < @bulk and $bulk[ $i ] =~ m{^Dump of file\s\Q$lib\E$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
268 ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
269 ( $i < @bulk and $bulk[ $i ] =~ m{^File Type:\s(.*)$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
270 ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
271
272 if ( $ext eq ".lib" ) {
273
274 my %deps;
275 while ( $i < @bulk ) {
276 my $line = $bulk[ $i ];
277 if ( 0 ) {
278 } elsif ( $line =~ m{^\s*[-/]defaultlib\:(.*)\s*$}i ) {
279 my $dep = $1;
280 # Normalize library name:
281 $dep = lc( $1 ); # Convert to lower case.
282 $dep =~ s{\A"(.*)"\z}{$1}; # Drop surrounding quotes (if any).
283 $dep =~ s{\.lib\z}{}; # Drop .lib suffix (if any).
284 $deps{ $dep } = 1;
285 } elsif ( $line =~ m{^\s*Linker Directives\s*$} ) {
286 } elsif ( $line =~ m{^\s*-+\s*$} ) {
287 } elsif ( $line =~ m{^\s*/alternatename\:.*$} ) {
288 } elsif ( $line =~ m{^\s*$} ) {
Jim Cownie4cc4bb42014-10-07 16:25:50 +0000289 } elsif ( $line =~ m{^\s*/FAILIFMISMATCH\:.*$} ) {
290 # This directive is produced only by _MSC_VER=1600
Jim Cownie5e8470a2013-09-27 10:38:44 +0000291 } elsif ( $line =~ m{^\s*Summary\s*$} ) {
292 last;
293 } else {
294 parse_error( $tool, @bulk, $i );
295 }; # if
296 ++ $i;
297 } # while
298 @deps = keys( %deps );
299
300 } else {
301
302 ( $i < @bulk and $bulk[ $i ] =~ m{\s*Image has the following dependencies\:$} )
303 or parse_error( $tool, @bulk, $i );
304 ++ $i;
305 while ( $i < @bulk ) {
306 my $line = $bulk[ $i ];
307 if ( 0 ) {
308 } elsif ( $line =~ m{^\s*$} ) {
309 # Ignore empty lines.
310 } elsif ( $line =~ m{^\s*(.*\.dll)$}i ) {
311 my $dep = lc( $1 );
312 push( @deps, $dep );
313 } elsif ( $line =~ m{^\s*Summary$} ) {
314 last;
315 } else {
316 parse_error( $tool, @bulk, $i );
317 }; # if
318 ++ $i;
319 }; # while
320
321 }; # if
322
323 return @deps;
324
325}; # sub get_deps_link
326
327
328# --------------------------------------------------------------------------------------------------
329# Main.
330# --------------------------------------------------------------------------------------------------
331
332# Parse command line.
333my $expected;
334my $bare;
335Getopt::Long::Configure( "permute" );
336get_options(
Jonathan Peyton4c91ad12016-01-26 19:44:31 +0000337 "os=s" => \$target_os,
338 "arch=s" => \$target_arch,
Jim Cownie5e8470a2013-09-27 10:38:44 +0000339 "bare" => \$bare,
340 "expected=s" => \$expected,
341);
342my @expected;
343if ( defined( $expected ) ) {
344 if ( $expected ne "none" ) {
345 @expected = sort( split( ",", $expected ) );
346 if ( $target_os eq "win" ) {
347 @expected = map( lc( $_ ), @expected );
348 }; # if
349 }; # if
350}; # if
351if ( @ARGV < 1 ) {
352 cmdline_error( "Specify a library name to check for dependencies" );
353}; # if
354if ( @ARGV > 1 ) {
355 cmdline_error( "Too many arguments" );
356}; # if
357my $lib = shift( @ARGV );
358if ( not -e $lib ){
359 runtime_error( "Specified file does not exist: \"$lib\"" );
360}; # if
361
362# Select appropriate get_deps implementation.
363if ( 0 ) {
Andrey Churbanovd315cea2015-01-16 12:54:51 +0000364} elsif ( $target_os eq "lin" ) {
Jim Cownie5e8470a2013-09-27 10:38:44 +0000365 *get_deps = \*get_deps_readelf;
366} elsif ( $target_os eq "mac" ) {
367 *get_deps = \*get_deps_otool;
368} elsif ( $target_os eq "win" ) {
369 *get_deps = \*get_deps_link;
370} else {
371 runtime_error( "OS \"$target_os\" not supported" );
372}; # if
373
374# Do the work.
375my @deps = sort( get_deps( $lib ) );
376if ( $bare ) {
377 print( map( "$_\n", @deps ) );
378} else {
379 info( "Dependencies:", @deps ? map( " $_", @deps ) : "(none)" );
380}; # if
381if ( defined( $expected ) ) {
382 my %deps = map( ( $_ => 1 ), @deps );
383 foreach my $dep ( @expected ) {
384 delete( $deps{ $dep } );
385 }; # foreach
386 my @unexpected = sort( keys( %deps ) );
387 if ( @unexpected ) {
388 runtime_error( "Unexpected dependencies:", map( " $_", @unexpected ) );
389 }; # if
390}; # if
391
392exit( 0 );
393
394__END__
395
396=pod
397
398=head1 NAME
399
400B<check-depends.pl> -- Check dependencies for a specified library.
401
402=head1 SYNOPSIS
403
404B<check-depends.pl> I<OPTIONS>... I<library>
405
406=head1 DESCRIPTION
407
408C<check-depends.pl> finds direct dependencies for a specified library. List of actual dependencies
409is sorted alphabetically and printed. If list of expected dependencies is specified, the scripts
410checks the library has only allowed dependencies. In case of not expected depndencies the script
411issues error message and exits with non-zero code.
412
413Linux* OS and OS X*: The script finds dependencies only for dymamic libraries. Windows* OS: The script
414finds dependencies for either static or dymamic libraries.
415
416The script uses external tools. On Linux* OS, it runs F<readelf>, on OS X* -- F<otool> (or F<otool64>),
417on Windows* OS -- F<link>.
418
419On Windows* OS dependencies are printed in lower case, case of expected dependencies ignored.
420
421=head1 OPTIONS
422
423=over
424
425=item B<--bare>
426
427Do not use fancy formatting; produce plain, bare output: just a list of libraries,
428a library per line.
429
430=item B<--expected=>I<list>
431
432I<list> is comma-separated list of expected dependencies (or C<none>).
433If C<--expected> option specified, C<check-depends.pl> checks the specified library
434has only expected dependencies.
435
436=item B<--os=>I<str>
437
438Specify target OS (tool to use) manually.
439Useful for cross-build, when host OS is not the same as target OS.
440I<str> should be either C<lin>, C<mac>, or C<win>.
441
442=back
443
444=head2 Standard Options
445
446=over
447
448=item B<--help>
449
450Print short help message and exit.
451
452=item B<--doc>
453
454=item B<--manual>
455
456Print full documentation and exit.
457
458=item B<--quiet>
459
460Do not output informational messages.
461
462=item B<--version>
463
464Print version and exit.
465
466=back
467
468=head1 ARGUMENTS
469
470=over
471
472=item I<library>
473
474A name of library to find or check dependencies.
475
476=back
477
478=head1 EXAMPLES
479
480Just print library dependencies (Windows* OS):
481
Jonathan Peytonf0efbb52015-06-01 02:41:44 +0000482 > check-depends.pl exports/win_32/lib/libompmd.dll
Jim Cownie5e8470a2013-09-27 10:38:44 +0000483 check-depends.pl: (i) Dependencies:
484 check-depends.pl: (i) kernel32.dll
485
486Print library dependencies, use bare output (Linux* OS):
487
488 $ check-depends.pl --bare exports/lin_32e/lib/libomp_db.so
489 libc.so.6
490 libdl.so.2
491 libpthread.so.0
492
493Check the library does not have any dependencies (OS X*):
494
Jonathan Peytonf0efbb52015-06-01 02:41:44 +0000495 $ check-depends.pl --expected=none exports/mac_32/lib/libomp.dylib
Jim Cownie5e8470a2013-09-27 10:38:44 +0000496 check-depends.pl: (i) Dependencies:
497 check-depends.pl: (i) /usr/lib/libSystem.B.dylib
498 check-depends.pl: (x) Unexpected dependencies:
499 check-depends.pl: (x) /usr/lib/libSystem.B.dylib
500 $ echo $?
501 2
502
503=cut
504
505# end of file #
506