xref: /titanic_51/usr/src/tools/scripts/check_rtime.pl (revision 791a814c934fcd4deb13b26c1f116ff283272a0d)
1#!/usr/perl5/bin/perl -w
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22
23#
24# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27#
28
29#
30# Check ELF information.
31#
32# This script descends a directory hierarchy inspecting ELF dynamic executables
33# and shared objects.  The general theme is to verify that common Makefile rules
34# have been used to build these objects.  Typical failures occur when Makefile
35# rules are re-invented rather than being inherited from "cmd/lib" Makefiles.
36#
37# As always, a number of components don't follow the rules, and these are
38# excluded to reduce this scripts output.  Pathnames used for this exclusion
39# assume this script is being run over a "proto" area.  The -a (all) option
40# skips any exclusions.
41#
42# By default any file that has conditions that should be reported is first
43# listed and then each condition follows.  The -o (one-line) option produces a
44# more terse output which is better for sorting/diffing with "nightly".
45#
46# NOTE: missing dependencies, symbols or versions are reported by running the
47# file through ldd(1).  As objects within a proto area are built to exist in a
48# base system, standard use of ldd(1) will bind any objects to dependencies
49# that exist in the base system.  It is frequently the case that newer objects
50# exist in the proto area that are required to satisfy other objects
51# dependencies, and without using these newer objects an ldd(1) will produce
52# misleading error messages.  To compensate for this, the -d option (or the
53# existence of the CODEMSG_WS/ROOT environment variables) cause the creation of
54# alternative dependency mappings via crle(1) configuration files that establish
55# any proto shared objects as alternatives to their base system location.  Thus
56# ldd(1) can be executed against these configuration files so that objects in a
57# proto area bind to their dependencies in the same proto area.
58
59
60# Define all global variables (required for strict)
61use vars  qw($SkipDirs $SkipFiles $SkipTextrelFiles $SkipDirectBindFiles);
62use vars  qw($SkipUndefFiles $SkipUnusedDeps);
63use vars  qw($SkipStabFiles $SkipNoExStkFiles $SkipCrleConf);
64use vars  qw($SkipUnusedSearchPath $SkipUnrefObject $SkipUnusedObject);
65use vars  qw($Prog $Mach $Isalist $Env $Ena64 $Tmpdir $Error $Gnuc);
66use vars  qw($UnusedPaths $LddNoU $Crle32 $Crle64 $Conf32 $Conf64);
67use vars  qw($SkipDirectBindDirs $SkipInterps $SkipSymSort $OldDeps %opt);
68
69use strict;
70
71
72# Define any directories we should skip completely.
73$SkipDirs = qr{
74	usr/lib/libc |			# optimized libc
75	usr/lib/rcm |			# 4426119
76	usr/perl5 |			# alan's taking care of these :-)
77	usr/src				# no need to look at shipped source
78}x;
79
80# Define any files we should skip completely.
81$SkipFiles = qr{ ^(?:
82	lddstub |			# lddstub has no dependencies
83	geniconvtbl\.so |		# 4384329
84	libssagent\.so\.1 |		# 4328854
85	libpsvcplugin_psr\.so\.1 |	# 4385799
86	libpsvcpolicy_psr\.so\.1 |	#  "  "
87	libpsvcpolicy\.so\.1 |		#  "  "
88	picl_slm\.so |			#  "  "
89	mod_ipp\.so |			# Apache loadable module
90	fptest |	# USIII specific extns. cause ldd noise on USII bld. m/c
91	grub
92	)$
93}x;
94
95# Define any files that are allowed text relocations.
96$SkipTextrelFiles = qr{ ^(?:
97	unix |				# kernel models are non-pic
98	mdb				# relocations against __RTC (dbx)
99	)$
100}x;
101
102# Define any directories or files that are allowed to have no direct bound
103# symbols
104$SkipDirectBindDirs = qr{
105	usr/ucb
106}x;
107
108$SkipDirectBindFiles = qr{ ^(?:
109	unix |
110	sbcp |
111	libproc.so.1 |
112	libnisdb.so.2
113	)$
114}x;
115
116# Define any files that are allowed undefined references.
117
118$SkipUndefFiles = qr{ ^(?:
119	libsvm\.so\.1 |			# libspmicommon.so.1 lacking
120	libnisdb\.so\.2			# C++
121	)$
122}x;
123
124# Define any files that have unused dependencies.
125$SkipUnusedDeps = qr{
126	lib/picl/plugins/ |		# require devtree dependencies
127	/lib/libp			# profile libc makes libm an unused
128}x;					#	dependency of standard libc
129
130# Define any objects that always look unused.
131$SkipUnusedObject = qr{
132	/libm_hwcap[0-9]+\.so\.2	# libm.so.2 dependency
133}x;
134
135# Define any files that should contain debugging information.
136$SkipStabFiles = qr{ ^(?:
137	unix
138	)$
139}x;
140
141# Define any files that don't require a non-executable stack definition.
142$SkipNoExStkFiles = qr{ ^(?:
143	forth |
144	unix |
145	multiboot
146	)$
147}x;
148
149# Identify any files that should be skipped when building a crle(1)
150# configuration file.  As the hwcap libraries can be loop-back mounted onto
151# libc, these can confuse crle(1) because of their identical dev/inode.
152$SkipCrleConf = qr{
153	lib/libc/libc_hwcap
154}x;
155
156# Skip "unused search path=" ldd(1) diagnostics.
157$SkipUnusedSearchPath = qr{
158	/usr/lib/fs/autofs.*\ from\ .automountd |		# dlopen()
159	/etc/ppp/plugins.*\ from\ .*pppd |			# dlopen()
160	/usr/lib/inet/ppp.*\ from\ .*pppd |			# dlopen()
161	/usr/sfw/lib.*\ from\ .*libipsecutil.so.1 |		# dlopen()
162	/usr/platform/.*rsmlib.*\ from\ .*librsm.so.2 |		# dlopen()
163	\$ORIGIN.*\ from\ .*fcode.so |				# dlopen()
164	/opt/VRTSvxvm/lib.*\ from\ .*libdiskmgt\.so\.1 |	# dlopen()
165	/usr/platform/.*\ from\ .*/usr/platform |		# picl
166	/usr/lib/picl/.*\ from\ .*/usr/platform |		# picl
167	/usr/platform/.*\ from\ .*/usr/lib/picl |		# picl
168	/usr/lib/smbsrv.*\ from\ .*libsmb\.so\.1 |		# future needs
169	/usr/lib/mps/secv1.*\ from\ .*libnss3\.so |		# non-OSNet
170	/usr/lib/mps.*\ from\ .*libnss3\.so |			# non-OSNet
171	/usr/lib/mps.*\ from\ .*libnssutil3.so |		# non-OSNET
172	/usr/sfw/lib.*\ from\ .*libdbus-1\.so\.3 |		# non-OSNet
173	/usr/sfw/lib.*\ from\ .*libdbus-glib-1\.so\.2 |		# non-OSNet
174	/usr/sfw/lib.*\ from\ .*libglib-2\.0\.so\.0 |		# non-OSNet
175	/usr/X11/lib.*\ from\ .*libglib-2\.0\.so\.0 |		# non-OSNet
176	/usr/sfw/lib.*\ from\ .*libgmodule-2\.0\.so\.0 |	# non-OSNet
177	/usr/X11/lib.*\ from\ .*libgmodule-2\.0\.so\.0 |	# non-OSNet
178	/usr/sfw/lib.*\ from\ .*libgnomevfs-2\.so\.0 |		# non-OSNet
179	/usr/sfw/lib.*\ from\ .*libgobject-2\.0\.so\.0 |	# non-OSNet
180	/usr/X11/lib.*\ from\ .*libgobject-2\.0\.so\.0 |	# non-OSNet
181	/usr/sfw/lib.*\ from\ .*libgthread-2\.0\.so\.0 |	# non-OSNet
182	/usr/X11/lib.*\ from\ .*libgthread-2\.0\.so\.0 |	# non-OSNet
183	/usr/sfw/lib.*\ from\ .*libcrypto\.so\.0\.9\.8 |	# non-OSNet
184	/usr/sfw/lib.*\ from\ .*libnetsnmp\.so\.15 |		# non-OSNet
185	/usr/sfw/lib.*\ from\ .*libgcc_s\.so\.1 |		# non-OSNet
186	/usr.*\ from\ .*tst\.gcc\.exe |				# gcc built
187	/usr/postgres/8.3/lib.*\ from\ .*libpq\.so\.5 |		# non-OSNET
188	/usr/sfw/lib.*\ from\ .*libpq\.so\.5			# non-OSNET
189}x;
190
191# Skip "unreferenced object=" ldd(1) diagnostics.
192$SkipUnrefObject = qr{
193	/libmapmalloc\.so\.1;\ unused\ dependency\ of |		# interposer
194	/libstdc\+\+\.so\.6;\ unused\ dependency\ of |		# gcc build
195	/libm\.so\.2.*\ of\ .*libstdc\+\+\.so\.6 |		# gcc build
196	/lib.*\ of\ .*/lib/picl/plugins/ |			# picl
197	/lib.*\ of\ .*libcimapi\.so |				# non-OSNET
198	/lib.*\ of\ .*libjvm\.so |				# non-OSNET
199	/lib.*\ of\ .*libnetsnmp\.so\.15 |			# non-OSNET
200	/lib.*\ of\ .*libnetsnmpagent\.so\.15 |			# non-OSNET
201	/lib.*\ of\ .*libnetsnmpmibs\.so\.15 |			# non-OSNET
202	/lib.*\ of\ .*libnetsnmphelpers\.so\.15 |		# non-OSNET
203	/lib.*\ of\ .*libnspr4\.so |				# non-OSNET
204	/lib.*\ of\ .*libsoftokn3\.so |				# non-OSNET
205	/lib.*\ of\ .*libspmicommon\.so\.1 |			# non-OSNET
206	/lib.*\ of\ .*libspmocommon\.so\.1 |			# non-OSNET
207	/lib.*\ of\ .*libssl3\.so |				# non-OSNET
208	/lib.*\ of\ .*libxml2\.so\.2 |				# non-OSNET
209	/lib.*\ of\ .*libxslt\.so\.1 |				# non-OSNET
210	/lib.*\ of\ .*libpq\.so\.4 |				# non-OSNET
211	/lib.*\ of\ .*libpython2\.4\.so\.1\.0 |			# non-OSNET
212	/lib.*\ of\ .*kcfd |					# interposer
213	/libpkcs11\.so\.1; .*\ of\ .*libkmf\.so\.1		# interposed
214}x;
215
216# Define any files that should only have unused (ldd -u) processing.
217$UnusedPaths = qr{
218	ucb/shutdown			# libucb interposes on libc and makes
219					# dependencies on libc seem unnecessary
220}x;
221
222# Define interpreters we should ignore.
223$SkipInterps = qr{
224	misc/krtld |
225	misc/amd64/krtld |
226	misc/sparcv9/krtld
227}x;
228
229# Catch libintl and libw, although ld(1) will bind to these and thus determine
230# they're needed, their content was moved into libc as of on297 build 7.
231# libsched was moved into libc as of on10 build 36.  libdl was moved into libc
232# as of on10 build 49.  libthread and libpthread were into libc as of on10 build
233# 53.  libdoor was moved into libc as of Nevada build 12.  librt and libaio were
234# moved into libc in Nevada build 44.
235$OldDeps = qr{ ^(?:
236	libintl\.so\.1 |
237	libw\.so\.1 |
238	libsched\.so\.1 |
239	libdl\.so\.1 |
240	libthread\.so\.1 |
241	libpthread\.so\.1 |
242	libdoor\.so\.1 |
243	librt\.so\.1 |
244	libaio\.so\.1
245	)$
246}x;
247
248# Files for which we skip checking of duplicate addresses in the
249# symbol sort sections. Such exceptions should be rare --- most code will
250# not have duplicate addresses, since it takes assember or a "#pragma weak"
251# to do such aliasing in C. C++ is different: The compiler generates aliases
252# for implementation reasons, and the mangled names used to encode argument
253# and return value types are difficult to handle well in mapfiles.
254# Furthermore, the Sun compiler and gcc use different and incompatible
255# name mangling conventions. Since ON must be buildable by either, we
256# would have to maintain two sets of mapfiles for each such object.
257# C++ use is rare in ON, so this is not worth pursuing.
258#
259$SkipSymSort = qr{ ^.*(?:
260	opt/SUNWdtrt/tst/common/pid/tst.weak2.exe |	# DTrace test
261	lib/amd64/libnsl\.so\.1 |			# C++
262	lib/sparcv9/libnsl\.so\.1 |			# C++
263	lib/sparcv9/libfru\.so\.1 |			# C++
264	usr/lib/sgml/nsgmls |				# C++
265	usr/lib/lms |					# C++
266	ld\.so\.1 |					# libc_pic.a user
267	lib/libsun_fc\.so\.1 |				# C++
268	lib/amd64/libsun_fc\.so\.1 |			# C++
269	lib/sparcv9/libsun_fc\.so\.1 			# C++
270	)$
271}x;
272
273use Getopt::Std;
274
275# -----------------------------------------------------------------------------
276
277# Reliably compare two OS revisions.  Arguments are <ver1> <op> <ver2>.
278# <op> is the string form of a normal numeric comparison operator.
279sub cmp_os_ver {
280	my @ver1 = split(/\./, $_[0]);
281	my $op = $_[1];
282	my @ver2 = split(/\./, $_[2]);
283
284	push @ver2, ("0") x $#ver1 - $#ver2;
285	push @ver1, ("0") x $#ver2 - $#ver1;
286
287	my $diff = 0;
288	while (@ver1 || @ver2) {
289		if (($diff = shift(@ver1) - shift(@ver2)) != 0) {
290			last;
291		}
292	}
293	return (eval "$diff $op 0" ? 1 : 0);
294}
295
296# This script relies on ldd returning output reflecting only the binary
297# contents.  But if LD_PRELOAD* environment variables are present, libraries
298# named by them will also appear in the output, disrupting our analysis.
299# So, before we get too far, scrub the environment.
300
301delete($ENV{LD_PRELOAD});
302delete($ENV{LD_PRELOAD_32});
303delete($ENV{LD_PRELOAD_64});
304
305# Establish a program name for any error diagnostics.
306chomp($Prog = `basename $0`);
307
308# Determine what machinery is available.
309$Mach = `uname -p`;
310$Isalist = `isalist`;
311$Env = "";
312if ($Mach =~ /sparc/) {
313	if ($Isalist =~ /sparcv9/) {
314		$Ena64 = "ok";
315	}
316} elsif ($Mach =~ /i386/) {
317	if ($Isalist =~ /amd64/) {
318		$Ena64 = "ok";
319	}
320}
321
322# Check that we have arguments.
323if ((getopts('ad:imosv', \%opt) == 0) || ($#ARGV == -1)) {
324	print "usage: $Prog [-a] [-d depdir] [-m] [-o] [-s] file | dir, ...\n";
325	print "\t[-a]\t\tprocess all files (ignore any exception lists)\n";
326	print "\t[-d dir]\testablish dependencies from under directory\n";
327	print "\t[-i]\t\tproduce dynamic table entry information\n";
328	print "\t[-m]\t\tprocess mcs(1) comments\n";
329	print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
330	print "\t[-s]\t\tprocess .stab and .symtab entries\n";
331	print "\t[-v]\t\tprocess version definition entries\n";
332	exit 1;
333} else {
334	my($Proto);
335
336	if ($opt{d}) {
337		# User specified dependency directory - make sure it exists.
338		if (! -d $opt{d}) {
339			print "$Prog: $opt{d} is not a directory\n";
340			exit 1;
341		}
342		$Proto = $opt{d};
343
344	} elsif ($ENV{CODEMGR_WS}) {
345		my($Root);
346
347		# Without a user specified dependency directory see if we're
348		# part of a codemanager workspace and if a proto area exists.
349		if (($Root = $ENV{ROOT}) && (-d $Root)) {
350			$Proto = $Root;
351		}
352	}
353
354	if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir)) {
355		$Tmpdir = "/tmp";
356	}
357
358	# Determine whether this is a __GNUC build.  If so, unused search path
359	# processing is disabled.
360	if (defined $ENV{__GNUC}) {
361		$Gnuc = 1;
362	} else {
363		$Gnuc = 0;
364	}
365
366	# Look for dependencies under $Proto.
367	if ($Proto) {
368		# To support alternative dependency mapping we'll need ldd(1)'s
369		# -e option.  This is relatively new (s81_30), so make sure
370		# ldd(1) is capable before gathering any dependency information.
371		if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) {
372			print "ldd: does not support -e, unable to ";
373			print "create alternative dependency mappingings.\n";
374			print "ldd: option added under 4390308 (s81_30).\n\n";
375		} else {
376			# Gather dependencies and construct a alternative
377			# dependency mapping via a crle(1) configuration file.
378			GetDeps($Proto, "/");
379			GenConf();
380		}
381	}
382
383	# To support unreferenced dependency detection we'll need ldd(1)'s -U
384	# option.  This is relatively new (4638070), and if not available we
385	# can still fall back to -u.  Even with this option, don't use -U with
386	# releases prior to 5.10 as the cleanup for -U use only got integrated
387	# into 5.10 under 4642023.  Note, that nightly doesn't typically set a
388	# RELEASE from the standard <env> files.  Users who wish to disable use
389	# of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file
390	# if using nightly, or otherwise establish it in their environment.
391	if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) {
392		$LddNoU = 1;
393	} else {
394		my($Release);
395
396		if (($Release = $ENV{RELEASE}) &&
397		    (cmp_os_ver($Release, "<", "5.10"))) {
398			$LddNoU = 1;
399		} else {
400			$LddNoU = 0;
401		}
402	}
403
404	# For each argument determine if we're dealing with a file or directory.
405	foreach my $Arg (@ARGV) {
406		# Ignore symbolic links.
407		if (-l $Arg) {
408			next;
409		}
410
411		if (!stat($Arg)) {
412			next;
413		}
414
415		# Process simple files.
416		if (-f _) {
417			my($RelPath) = $Arg;
418			my($File) = $Arg;
419			my($Secure) = 0;
420
421			$RelPath =~ s!^.*/!./!;
422			$File =~ s!^.*/!!;
423
424			if (-u _ || -g _) {
425				$Secure = 1;
426			}
427
428			ProcFile($Arg, $RelPath, $File, $Secure);
429			next;
430		}
431		# Process directories.
432		if (-d _) {
433			ProcDir($Arg, ".");
434			next;
435		}
436
437		print "$Arg is not a file or directory\n";
438		$Error = 1;
439	}
440
441	# Cleanup
442	CleanUp();
443}
444
445$Error = 0;
446
447# Clean up any temporary files.
448sub CleanUp {
449	if ($Crle64) {
450		unlink $Crle64;
451	}
452	if ($Conf64) {
453		unlink $Conf64;
454	}
455	if ($Crle32) {
456		unlink $Crle32;
457	}
458	if ($Conf32) {
459		unlink $Conf32;
460	}
461}
462
463# Create an output message, either a one-liner (under -o) or preceded by the
464# files relative pathname as a title.
465sub OutMsg {
466	my($Ttl, $Path, $Msg) = @_;
467
468	if ($opt{o}) {
469		$Msg =~ s/^[ \t]*//;
470		print "$Path: $Msg\n";
471	} else {
472		if ($Ttl eq 0) {
473			print "==== $Path ====\n";
474		}
475		print "$Msg\n";
476	}
477}
478
479# Determine whether this a ELF dynamic object and if so investigate its runtime
480# attributes.
481sub ProcFile {
482	my($FullPath, $RelPath, $File, $Secure) = @_;
483	my(@Elf, @Ldd, $Dyn, $Intp, $Dll, $Ttl, $Sym, $Interp, $Stack);
484	my($Sun, $Relsz, $Pltsz, $Tex, $Stab, $Strip, $Lddopt, $SymSort);
485	my($Val, $Header, $SkipLdd, $IsX86, $RWX, $UnDep);
486	my($HasDirectBinding, $HasVerdef);
487
488	# Ignore symbolic links.
489	if (-l $FullPath) {
490		return;
491	}
492
493	$Ttl = 0;
494	@Ldd = 0;
495
496	# Determine whether we have access to inspect the file.
497	if (!(-r $FullPath)) {
498		OutMsg($Ttl++, $RelPath,
499		    "\tunable to inspect file: permission denied");
500		return;
501	}
502
503	# Determine if this is a file we don't care about.
504	if (!$opt{a}) {
505		if ($File =~ $SkipFiles) {
506			return;
507		}
508	}
509
510	# Determine whether we have a executable (static or dynamic) or a
511	# shared object.
512	@Elf = split(/\n/, `elfdump -epdicyv $FullPath 2>&1`);
513
514	$Dyn = $Intp = $Dll = $Stack = $IsX86 = $RWX = 0;
515	$Interp = 1;
516	$Header = 'None';
517	foreach my $Line (@Elf) {
518		# If we have an invalid file type (which we can tell from the
519		# first line), or we're processing an archive, bail.
520		if ($Header eq 'None') {
521			if (($Line =~ /invalid file/) ||
522			    ($Line =~ /$FullPath(.*):/)) {
523				return;
524			}
525		}
526
527		if ($Line =~ /^ELF Header/) {
528			$Header = 'Ehdr';
529
530		} elsif ($Line =~ /^Program Header/) {
531			$Header = 'Phdr';
532			$RWX = 0;
533
534		} elsif ($Line =~ /^Interpreter/) {
535			$Header = 'Intp';
536
537		} elsif ($Line =~ /^Dynamic Section/) {
538			# A dynamic section indicates we're a dynamic object
539			# (this makes sure we don't check static executables).
540			$Dyn = 1;
541
542		} elsif (($Header eq 'Ehdr') && ($Line =~ /e_type:/)) {
543			# The e_type field indicates whether this file is a
544			# shared object (ET_DYN) or an executable (ET_EXEC).
545			if ($Line =~ /ET_DYN/) {
546				$Dll = 1;
547			} elsif ($Line !~ /ET_EXEC/) {
548				return;
549			}
550		} elsif (($Header eq 'Ehdr') && ($Line =~ /ei_class:/)) {
551			# If we encounter a 64-bit object, but we're not running
552			# on a 64-bit system, suppress calling ldd(1).
553			if (($Line =~ /ELFCLASS64/) && !$Ena64) {
554				$SkipLdd = 1;
555			}
556		} elsif (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) {
557			# If it's a X86 object, we need to enforce RW- data.
558			if (($Line =~ /(EM_AMD64|EM_386)/)) {
559				$IsX86 = 1;
560			}
561		} elsif (($Header eq 'Phdr') &&
562		    ($Line =~ /\[ PF_X  PF_W  PF_R \]/)) {
563			# RWX segment seen.
564			$RWX = 1;
565
566		} elsif (($Header eq 'Phdr') &&
567		    ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) {
568			# Seen an RWX PT_LOAD segment.
569			if ($File !~ $SkipNoExStkFiles) {
570				OutMsg($Ttl++, $RelPath,
571				    "\tapplication requires non-executable " .
572				    "data\t<no -Mmapfile_noexdata?>");
573			}
574
575		} elsif (($Header eq 'Phdr') &&
576		    ($Line =~ /\[ PT_SUNWSTACK \]/)) {
577			# This object defines a non-executable stack.
578			$Stack = 1;
579
580		} elsif (($Header eq 'Intp') && !$opt{a} &&
581		    ($Line =~ $SkipInterps)) {
582			# This object defines an interpretor we should skip.
583			$Interp = 0;
584		}
585	}
586
587	# Determine whether this ELF executable or shared object has a
588	# conforming mcs(1) comment section.  If the correct $(POST_PROCESS)
589	# macros are used, only a 3 or 4 line .comment section should exist
590	# containing one or two "@(#)SunOS" identifying comments (one comment
591	# for a non-debug build, and two for a debug build). The results of
592	# the following split should be three or four lines, the last empty
593	# line being discarded by the split.
594	if ($opt{m}) {
595		my(@Mcs, $Con, $Dev);
596
597		@Mcs = split(/\n/, `mcs -p $FullPath 2>&1`);
598
599		$Con = $Dev = $Val = 0;
600		foreach my $Line (@Mcs) {
601			$Val++;
602
603			if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) {
604				$Con = 1;
605				last;
606			}
607			if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) {
608				$Dev = 1;
609				next;
610			}
611			if (($Dev == 0) && ($Val == 4)) {
612				$Con = 1;
613				last;
614			}
615			if (($Dev == 1) && ($Val == 5)) {
616				$Con = 1;
617				last;
618			}
619		}
620		if ($opt{m} && ($Con == 1)) {
621			OutMsg($Ttl++, $RelPath,
622			    "\tnon-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>");
623		}
624	}
625
626	# Applications should contain a non-executable stack definition.
627	if (($Dll == 0) && ($Stack == 0)) {
628		if (!$opt{a}) {
629			if ($File =~ $SkipNoExStkFiles) {
630				goto DYN;
631			}
632		}
633		OutMsg($Ttl++, $RelPath,
634		    "\tapplication requires non-executable stack\t<no -Mmapfile_noexstk?>");
635	}
636
637DYN:
638	# Having caught any static executables in the mcs(1) check and non-
639	# executable stack definition check, continue with dynamic objects
640	# from now on.
641	if ($Dyn eq 0) {
642		return;
643	}
644
645	# Only use ldd unless we've encountered an interpreter that should
646	# be skipped.
647	if (!$SkipLdd && $Interp) {
648		my $LDDFullPath = $FullPath;
649
650		if ($Secure) {
651			# The execution of a secure application over an nfs file
652			# system mounted nosuid will result in warning messages
653			# being sent to /var/adm/messages.  As this type of
654			# environment can occur with root builds, move the file
655			# being investigated to a safe place first.  In addition
656			# remove its secure permission so that it can be
657			# influenced by any alternative dependency mappings.
658
659			my($TmpPath) = "$Tmpdir/$File";
660
661			system('cp', $LDDFullPath, $TmpPath);
662			chmod 0777, $TmpPath;
663			$LDDFullPath = $TmpPath;
664		}
665
666		# Use ldd(1) to determine the objects relocatability and use.
667		# By default look for all unreferenced dependencies.  However,
668		# some objects have legitimate dependencies that they do not
669		# reference.
670		if ($LddNoU || ($RelPath =~ $UnusedPaths)) {
671			$Lddopt = "-ru";
672		} else {
673			$Lddopt = "-rU";
674		}
675		@Ldd = split(/\n/, `ldd $Lddopt $Env $LDDFullPath 2>&1`);
676		if ($Secure) {
677			unlink $LDDFullPath;
678		}
679	}
680
681	$Val = 0;
682	$Sym = 5;
683	$UnDep = 1;
684
685	foreach my $Line (@Ldd) {
686
687		if ($Val == 0) {
688			$Val = 1;
689			# Make sure ldd(1) worked.  One possible failure is that
690			# this is an old ldd(1) prior to -e addition (4390308).
691			if ($Line =~ /usage:/) {
692				$Line =~ s/$/\t<old ldd(1)?>/;
693				OutMsg($Ttl++, $RelPath, $Line);
694				last;
695			} elsif ($Line =~ /execution failed/) {
696				OutMsg($Ttl++, $RelPath, $Line);
697				last;
698			}
699
700			# It's possible this binary can't be executed, ie. we've
701			# found a sparc binary while running on an intel system,
702			# or a sparcv9 binary on a sparcv7/8 system.
703			if ($Line =~ /wrong class/) {
704				OutMsg($Ttl++, $RelPath,
705				    "\thas wrong class or data encoding");
706				next;
707			}
708
709			# Historically, ldd(1) likes executable objects to have
710			# their execute bit set.  Note that this test isn't
711			# applied unless the -a option is in effect, as any
712			# non-executable files are skipped by default to reduce
713			# the cost of running this script.
714			if ($Line =~ /not executable/) {
715				OutMsg($Ttl++, $RelPath,
716				    "\tis not executable");
717				next;
718			}
719		}
720
721		# Look for "file" or "versions" that aren't found.  Note that
722		# these lines will occur before we find any symbol referencing
723		# errors.
724		if (($Sym == 5) && ($Line =~ /not found\)/)) {
725			if ($Line =~ /file not found\)/) {
726				$Line =~ s/$/\t<no -zdefs?>/;
727			}
728			OutMsg($Ttl++, $RelPath, $Line);
729			next;
730		}
731		# Look for relocations whose symbols can't be found.  Note, we
732		# only print out the first 5 relocations for any file as this
733		# output can be excessive.
734		if ($Sym && ($Line =~ /symbol not found/)) {
735			# Determine if this file is allowed undefined
736			# references.
737			if ($Sym == 5) {
738				if (!$opt{a}) {
739					if ($File =~ $SkipUndefFiles) {
740						$Sym = 0;
741						next;
742					}
743				}
744			}
745			if ($Sym-- == 1) {
746				if (!$opt{o}) {
747					OutMsg($Ttl++, $RelPath,
748					    "\tcontinued ...");
749				}
750				next;
751			}
752			# Just print the symbol name.
753			$Line =~ s/$/\t<no -zdefs?>/;
754			OutMsg($Ttl++, $RelPath, $Line);
755			next;
756		}
757		# Look for any unused search paths.
758		if ($Line =~ /unused search path=/) {
759			# Note, skip this comparison for __GNUC builds, as the
760			# gnu compilers insert numerous unused search paths.
761			if ($Gnuc == 1) {
762				next;
763			}
764			if (!$opt{a}) {
765				if ($Line =~ $SkipUnusedSearchPath) {
766					next;
767				}
768			}
769			if ($Secure) {
770				$Line =~ s!$Tmpdir/!!;
771			}
772			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove search path?>/;
773			OutMsg($Ttl++, $RelPath, $Line);
774			next;
775		}
776		# Look for unreferenced dependencies.  Note, if any unreferenced
777		# objects are ignored, then set $UnDep so as to suppress any
778		# associated unused-object messages.
779		if ($Line =~ /unreferenced object=/) {
780			if (!$opt{a}) {
781				if ($Line =~ $SkipUnrefObject) {
782					$UnDep = 0;
783					next;
784				}
785			}
786			if ($Secure) {
787				$Line =~ s!$Tmpdir/!!;
788			}
789			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/;
790			OutMsg($Ttl++, $RelPath, $Line);
791			next;
792		}
793		# Look for any unused dependencies.
794		if ($UnDep && ($Line =~ /unused/)) {
795			if (!$opt{a}) {
796				if ($RelPath =~ $SkipUnusedDeps) {
797					next;
798				}
799				if ($Line =~ $SkipUnusedObject) {
800					next;
801				}
802			}
803			if ($Secure) {
804				$Line =~ s!$Tmpdir/!!;
805			}
806			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/;
807			OutMsg($Ttl++, $RelPath, $Line);
808			next;
809		}
810	}
811
812	# Reuse the elfdump(1) data to investigate additional dynamic linking
813	# information.
814
815	$Sun = $Relsz = $Pltsz = $Dyn = $Stab = $SymSort = 0;
816	$Tex = $Strip = 1;
817	$HasDirectBinding = 0;
818	$HasVerdef = 0;
819
820	$Header = 'None';
821ELF:	foreach my $Line (@Elf) {
822		# We're only interested in the section headers and the dynamic
823		# section.
824		if ($Line =~ /^Section Header/) {
825			$Header = 'Shdr';
826
827			if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) {
828				# This object has a combined relocation section.
829				$Sun = 1;
830
831			} elsif (($Stab == 0) && ($Line =~ /\.stab/)) {
832				# This object contain .stabs sections
833				$Stab = 1;
834			} elsif (($SymSort == 0) &&
835				 ($Line =~ /\.SUNW_dyn(sym)|(tls)sort/)) {
836				# This object contains a symbol sort section
837				$SymSort = 1;
838			}
839
840			if (($Strip == 1) && ($Line =~ /\.symtab/)) {
841				# This object contains a complete symbol table.
842				$Strip = 0;
843			}
844			next;
845
846		} elsif ($Line =~ /^Dynamic Section/) {
847			$Header = 'Dyn';
848			next;
849		} elsif ($Line =~ /^Syminfo Section/) {
850			$Header = 'Syminfo';
851			next;
852		} elsif ($Line =~ /^Version Definition Section/) {
853			$HasVerdef = 1;
854			next;
855		} elsif (($Header ne 'Dyn') && ($Header ne 'Syminfo')) {
856			next;
857		}
858
859		# Look into the Syminfo section.
860		# Does this object have at least one Directly Bound symbol?
861		if (($Header eq 'Syminfo')) {
862			my(@Symword);
863
864			if ($HasDirectBinding == 1) {
865				next;
866			}
867
868			@Symword = split(' ', $Line);
869
870			if (!defined($Symword[1])) {
871				next;
872			}
873			if ($Symword[1] =~ /B/) {
874				$HasDirectBinding = 1;
875			}
876			next;
877		}
878
879		# Does this object contain text relocations.
880		if ($Tex && ($Line =~ /TEXTREL/)) {
881			# Determine if this file is allowed text relocations.
882			if (!$opt{a}) {
883				if ($File =~ $SkipTextrelFiles) {
884					$Tex = 0;
885					next ELF;
886				}
887			}
888			OutMsg($Ttl++, $RelPath,
889			    "\tTEXTREL .dynamic tag\t\t\t<no -Kpic?>");
890			$Tex = 0;
891			next;
892		}
893
894		# Does this file have any relocation sections (there are a few
895		# psr libraries with no relocations at all, thus a .SUNW_reloc
896		# section won't exist either).
897		if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) {
898			$Relsz = hex((split(' ', $Line))[2]);
899			next;
900		}
901
902		# Does this file have any plt relocations.  If the plt size is
903		# equivalent to the total relocation size then we don't have
904		# any relocations suitable for combining into a .SUNW_reloc
905		# section.
906		if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) {
907			$Pltsz = hex((split(' ', $Line))[2]);
908			next;
909		}
910
911		# Does this object have any dependencies.
912		if ($Line =~ /NEEDED/) {
913			my($Need) = (split(' ', $Line))[3];
914
915			if ($Need =~ $OldDeps) {
916				# Catch any old (unnecessary) dependencies.
917				OutMsg($Ttl++, $RelPath,
918				    "\tNEEDED=$Need\t<dependency no longer necessary>");
919			} elsif ($opt{i}) {
920				# Under the -i (information) option print out
921				# any useful dynamic entries.
922				OutMsg($Ttl++, $RelPath, "\tNEEDED=$Need");
923			}
924			next;
925		}
926
927		# Is this object built with -B direct flag on?
928		if ($Line =~ / DIRECT /) {
929			$HasDirectBinding = 1;
930		}
931
932		# Does this object specify a runpath.
933		if ($opt{i} && ($Line =~ /RPATH/)) {
934			my($Rpath) = (split(' ', $Line))[3];
935			OutMsg($Ttl++, $RelPath, "\tRPATH=$Rpath");
936			next;
937		}
938	}
939
940	# A shared object, that contains non-plt relocations, should have a
941	# combined relocation section indicating it was built with -z combreloc.
942	if ($Dll && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) {
943		OutMsg($Ttl++, $RelPath,
944		    "\tSUNW_reloc section missing\t\t<no -zcombreloc?>");
945	}
946
947	# No objects released to a customer should have any .stabs sections
948	# remaining, they should be stripped.
949	if ($opt{s} && $Stab) {
950		if (!$opt{a}) {
951			if ($File =~ $SkipStabFiles) {
952				goto DONESTAB;
953			}
954		}
955		OutMsg($Ttl++, $RelPath,
956		    "\tdebugging sections should be deleted\t<no strip -x?>");
957	}
958
959	# Identify an object that is not built with either -B direct or
960	# -z direct.
961	if (($RelPath =~ $SkipDirectBindDirs) ||
962	    ($File =~ $SkipDirectBindFiles)) {
963		goto DONESTAB;
964	}
965	if ($Relsz && ($HasDirectBinding == 0)) {
966		OutMsg($Ttl++, $RelPath,
967		    "\tobject has no direct bindings\t<no -B direct or -z direct?>");
968	}
969
970DONESTAB:
971
972	# All objects should have a full symbol table to provide complete
973	# debugging stack traces.
974	if ($Strip) {
975		OutMsg($Ttl++, $RelPath,
976		    "\tsymbol table should not be stripped\t<remove -s?>");
977	}
978
979	# If there are symbol sort sections in this object, report on
980	# any that have duplicate addresses.
981	ProcSymSort($FullPath, $RelPath, \$Ttl) if $SymSort;
982
983	# If -v was specified, and the object has a version definition
984	# section, generate output showing each public symbol and the
985	# version it belongs to.
986	ProcVerdef($FullPath, $RelPath, \$Ttl) if $HasVerdef && $opt{v};
987}
988
989
990## ProcSymSortOutMsg(RefTtl, RelPath, secname, addr, names...)
991#
992# Call OutMsg for a duplicate address error in a symbol sort
993# section
994#
995sub ProcSymSortOutMsg {
996	my($RefTtl, $RelPath, $secname, $addr, @names) = @_;
997
998	OutMsg($$RefTtl++, $RelPath,
999	    "$secname: duplicate $addr: ". join(', ', @names));
1000}
1001
1002
1003## ProcSymSort(FullPath, RelPath)
1004#
1005# Examine the symbol sort sections for the given object and report
1006# on any duplicate addresses found.  Ideally, mapfile directives
1007# should be used when building objects that have multiple symbols
1008# with the same address so that only one of them appears in the sort
1009# section. This saves space, reduces user confusion, and ensures that
1010# libproc and debuggers always display public names instead of symbols
1011# that are merely implementation details.
1012#
1013sub ProcSymSort {
1014
1015	my($FullPath, $RelPath, $RefTtl) = @_;
1016
1017	# If this object is exempt from checking, return quietly
1018	return if ($FullPath =~ $SkipSymSort);
1019
1020
1021	open(SORT, "elfdump -S $FullPath|") ||
1022	    die "$Prog: Unable to execute elfdump (symbol sort sections)\n";
1023
1024	my $line;
1025	my $last_addr;
1026	my @dups = ();
1027	my $secname;
1028	while ($line = <SORT>) {
1029		chomp $line;
1030
1031		next if ($line eq '');
1032
1033		# If this is a header line, pick up the section name
1034		if ($line =~ /^Symbol Sort Section:\s+([^\s]+)\s+/) {
1035			$secname = $1;
1036
1037			# Every new section is followed by a column header line
1038			$line = <SORT>;		# Toss header line
1039
1040			# Flush anything left from previous section
1041			ProcSymSortOutMsg($RefTtl, $RelPath, $secname,
1042			    $last_addr, @dups) if (scalar(@dups) > 1);
1043
1044			# Reset variables for new sort section
1045			$last_addr = '';
1046			@dups = ();
1047
1048			next;
1049		}
1050
1051		# Process symbol line
1052		my @fields = split /\s+/, $line;
1053		my $new_addr = $fields[2];
1054		my $new_type = $fields[8];
1055		my $new_name = $fields[9];
1056
1057		if ($new_type eq 'UNDEF') {
1058		    OutMsg($RefTtl++, $RelPath,
1059		        "$secname: unexpected UNDEF symbol " .
1060			"(link-editor error): $new_name");
1061		    next;
1062		}
1063
1064		if ($new_addr eq $last_addr) {
1065			push @dups, $new_name;
1066		} else {
1067			ProcSymSortOutMsg($RefTtl, $RelPath, $secname,
1068			    $last_addr, @dups) if (scalar(@dups) > 1);
1069			@dups = ( $new_name );
1070			$last_addr = $new_addr;
1071		}
1072	}
1073
1074	ProcSymSortOutMsg($RefTtl, $RelPath, $secname, $last_addr, @dups)
1075		if (scalar(@dups) > 1);
1076
1077	close SORT;
1078}
1079
1080
1081## ProcVerdef(FullPath, RelPath)
1082#
1083# Examine the version definition section for the given object and report
1084# each public symbol along with the version it belongs to.
1085#
1086sub ProcVerdef {
1087
1088	my($FullPath, $RelPath, $RefTtl) = @_;
1089	my $line;
1090	my $cur_ver = '';
1091	my $tab = $opt{o} ? '' : "\t";
1092
1093	# pvs -dov provides information about the versioning hierarchy
1094	# in the file. Lines are of the format:
1095	#	path - version[XXX];
1096	# where [XXX] indicates optional information, such as flags
1097	# or inherited versions.
1098	#
1099	# Private versions are allowed to change freely, so ignore them.
1100	open(PVS, "pvs -dov $FullPath|") ||
1101	    die "$Prog: Unable to execute pvs (version definition section)\n";
1102
1103	while ($line = <PVS>) {
1104		chomp $line;
1105
1106		if ($line =~ /^[^\s]+\s+-\s+([^;]+)/) {
1107			my $ver = $1;
1108
1109			next if $ver =~ /private/i;
1110			OutMsg($$RefTtl++, $RelPath, "${tab}VERDEF=$ver");
1111		}
1112	}
1113	close PVS;
1114
1115	# pvs -dos lists the symbols assigned to each version definition.
1116	# Lines are of the format:
1117	#	path - version: symbol;
1118	#	path - version: symbol (size);
1119	# where the (size) is added to data items, but not for functions.
1120	# We strip off the size, if present.
1121
1122	open(PVS, "pvs -dos $FullPath|") ||
1123	    die "$Prog: Unable to execute pvs (version definition section)\n";
1124	while ($line = <PVS>) {
1125		chomp $line;
1126		if ($line =~ /^[^\s]+\s+-\s+([^:]+):\s*([^\s;]+)/) {
1127		    my $ver = $1;
1128		    my $sym = $2;
1129
1130		    next if $ver =~ /private/i;
1131
1132		    if ($opt{o}) {
1133			OutMsg($$RefTtl++, $RelPath,
1134			       "VERSION=$ver, SYMBOL=$sym");
1135		    } else {
1136			if ($cur_ver ne $ver) {
1137			    OutMsg($$RefTtl++, $RelPath, "\tVERSION=$ver");
1138			    $cur_ver = $ver;
1139			}
1140			OutMsg($$RefTtl++, $RelPath, "\t\tSYMBOL=$sym");
1141		    }
1142		}
1143	}
1144
1145	close PVS;
1146}
1147
1148
1149sub ProcDir {
1150	my($FullDir, $RelDir) = @_;
1151	my($NewFull, $NewRel);
1152
1153	# Determine if this is a directory we don't care about.
1154	if (!$opt{a}) {
1155		if ($RelDir =~ $SkipDirs) {
1156			return;
1157		}
1158	}
1159
1160	# Open the directory and read each entry, omit files starting with "."
1161	if (opendir(DIR, $FullDir)) {
1162		foreach my $Entry (readdir(DIR)) {
1163			if ($Entry =~ /^\./) {
1164				next;
1165			}
1166			$NewFull = "$FullDir/$Entry";
1167
1168			# Ignore symlinks.
1169			if (-l $NewFull) {
1170				next;
1171			}
1172			if (!stat($NewFull)) {
1173				next;
1174			}
1175			$NewRel = "$RelDir/$Entry";
1176
1177			# Descend into and process any directories.
1178			if (-d _) {
1179				ProcDir($NewFull, $NewRel);
1180				next;
1181			}
1182
1183			# Typically dynamic objects are executable, so we can
1184			# reduce the overall cost of this script (a lot!) by
1185			# screening out non-executables here, rather than pass
1186			# them to file(1) later.  However, it has been known
1187			# for shared objects to be mistakenly left non-
1188			# executable, so with -a let all files through so that
1189			# this requirement can be verified (see ProcFile()).
1190			if (!$opt{a}) {
1191				if (! -x _) {
1192					next;
1193				}
1194			}
1195
1196			# Process any standard files.
1197			if (-f _) {
1198				my($Secure) = 0;
1199
1200				if (-u _ || -g _) {
1201					$Secure = 1;
1202				}
1203
1204				ProcFile($NewFull, $NewRel, $Entry, $Secure);
1205				next;
1206			}
1207
1208		}
1209		closedir(DIR);
1210	}
1211}
1212
1213# Create a crle(1) script for any 64-bit dependencies we locate.  A runtime
1214# configuration file will be generated to establish alternative dependency
1215# mappings for all these dependencies.
1216
1217sub Entercrle64 {
1218	my($FullDir, $RelDir, $Entry) = @_;
1219
1220	if (!$Crle64) {
1221		# Create and initialize the script if is doesn't already exit.
1222
1223		$Crle64 = "$Tmpdir/$Prog.crle64.$$";
1224		open(CRLE64, "> $Crle64") ||
1225			die "$Prog: open failed: $Crle64: $!";
1226
1227		print CRLE64 "#!/bin/sh\ncrle -64\\\n";
1228	}
1229	print CRLE64 "\t-o $FullDir -a $RelDir/$Entry \\\n";
1230}
1231
1232# Create a crle(1) script for any 32-bit dependencies we locate.  A runtime
1233# configuration file will be generated to establish alternative dependency
1234# mappings for all these dependencies.
1235
1236sub Entercrle32 {
1237	my($FullDir, $RelDir, $Entry) = @_;
1238
1239	if (!$Crle32) {
1240		# Create and initialize the script if is doesn't already exit.
1241
1242		$Crle32 = "$Tmpdir/$Prog.crle32.$$";
1243		open(CRLE32, "> $Crle32") ||
1244			die "$Prog: open failed: $Crle32: $!";
1245
1246		print CRLE32 "#!/bin/sh\ncrle \\\n";
1247	}
1248	print CRLE32 "\t-o $FullDir -a $RelDir/$Entry \\\n";
1249}
1250
1251# Having finished gathering dependencies, complete any crle(1) scripts and
1252# execute them to generate the associated runtime configuration files.  In
1253# addition establish the environment variable required to pass the configuration
1254# files to ldd(1).
1255
1256sub GenConf {
1257	if ($Crle64) {
1258		$Conf64 = "$Tmpdir/$Prog.conf64.$$";
1259		print CRLE64 "\t-c $Conf64\n";
1260
1261		chmod 0755, $Crle64;
1262		close CRLE64;
1263
1264		if (system($Crle64)) {
1265			undef $Conf64;
1266		}
1267	}
1268	if ($Crle32) {
1269		$Conf32 = "$Tmpdir/$Prog.conf32.$$";
1270		print CRLE32 "\t-c $Conf32\n";
1271
1272		chmod 0755, $Crle32;
1273		close CRLE32;
1274
1275		if (system($Crle32)) {
1276			undef $Conf32;
1277		}
1278	}
1279
1280	if ($Crle64 && $Conf64 && $Crle32 && $Conf32) {
1281		$Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32";
1282	} elsif ($Crle64 && $Conf64) {
1283		$Env = "-e LD_FLAGS=config_64=$Conf64";
1284	} elsif ($Crle32 && $Conf32) {
1285		$Env = "-e LD_FLAGS=config_32=$Conf32";
1286	}
1287}
1288
1289# Recurse through a directory hierarchy looking for appropriate dependencies.
1290
1291sub GetDeps {
1292	my($FullDir, $RelDir) = @_;
1293	my($NewFull);
1294
1295	# Open the directory and read each entry, omit files starting with "."
1296	if (opendir(DIR, $FullDir)) {
1297		 foreach my $Entry (readdir(DIR)) {
1298			if ($Entry =~ /^\./) {
1299				next;
1300			}
1301			$NewFull = "$FullDir/$Entry";
1302
1303			# We need to follow links so that any dependencies
1304			# are expressed in all their available forms.
1305			# Bail on symlinks like 32 -> .
1306			if (-l $NewFull) {
1307				if (readlink($NewFull) =~ /^\.$/) {
1308					next;
1309				}
1310			}
1311			if (!stat($NewFull)) {
1312				next;
1313			}
1314
1315			if (!$opt{a}) {
1316				if ($NewFull =~ $SkipCrleConf) {
1317					next;
1318				}
1319			}
1320
1321			# If this is a directory descend into it.
1322			if (-d _) {
1323				my($NewRel);
1324
1325				if ($RelDir =~ /^\/$/) {
1326					$NewRel = "$RelDir$Entry";
1327				} else {
1328					$NewRel = "$RelDir/$Entry";
1329				}
1330
1331				GetDeps($NewFull, $NewRel);
1332				next;
1333			}
1334
1335			# If this is a regular file determine if its a
1336			# valid ELF dependency.
1337			if (-f _) {
1338				my($File);
1339
1340				# Typically shared object dependencies end with
1341				# ".so" or ".so.?", hence we can reduce the cost
1342				# of this script (a lot!) by screening out files
1343				# that don't follow this pattern.
1344				if (!$opt{a}) {
1345					if ($Entry !~ /\.so(?:\.\d+)*$/) {
1346						next;
1347					}
1348				}
1349
1350				$File = `file $NewFull`;
1351				if ($File !~ /dynamic lib/) {
1352					next;
1353				}
1354
1355				if ($File =~ /32-bit/) {
1356					Entercrle32($FullDir, $RelDir, $Entry);
1357				} elsif ($Ena64) {
1358					Entercrle64($FullDir, $RelDir, $Entry);
1359				}
1360				next;
1361			}
1362		}
1363		closedir(DIR);
1364	}
1365}
1366exit $Error
1367