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