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