usr/src/tools/scripts/check_rtime.pl
changeset 0 68f95e015346
child 524 259d2acc2f55
equal deleted inserted replaced
-1:000000000000 0:68f95e015346
       
     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, Version 1.0 only
       
     7 # (the "License").  You may not use this file except in compliance
       
     8 # with the License.
       
     9 #
       
    10 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
    11 # or http://www.opensolaris.org/os/licensing.
       
    12 # See the License for the specific language governing permissions
       
    13 # and limitations under the License.
       
    14 #
       
    15 # When distributing Covered Code, include this CDDL HEADER in each
       
    16 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    17 # If applicable, add the following below this CDDL HEADER, with the
       
    18 # fields enclosed by brackets "[]" replaced with your own identifying
       
    19 # information: Portions Copyright [yyyy] [name of copyright owner]
       
    20 #
       
    21 # CDDL HEADER END
       
    22 #
       
    23 #
       
    24 # Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
       
    25 # Use is subject to license terms.
       
    26 #
       
    27 # ident	"%Z%%M%	%I%	%E% SMI"
       
    28 #
       
    29 # Check ELF information.
       
    30 #
       
    31 # This script descends a directory hierarchy inspecting ELF dynamic executables
       
    32 # and shared objects.  The general theme is to verify that common Makefile rules
       
    33 # have been used to build these objects.  Typical failures occur when Makefile
       
    34 # rules are re-invented rather than being inherited from "cmd/lib" Makefiles.
       
    35 #
       
    36 # As always, a number of components don't follow the rules, and these are
       
    37 # excluded to reduce this scripts output.  Pathnames used for this exclusion
       
    38 # assume this script is being run over a "proto" area.  The -a (all) option
       
    39 # skips any exclusions.
       
    40 #
       
    41 # By default any file that has conditions that should be reported is first
       
    42 # listed and then each condition follows.  The -o (one-line) option produces a
       
    43 # more terse output which is better for sorting/diffing with "nightly".
       
    44 #
       
    45 # NOTE: missing dependencies, symbols or versions are reported by running the
       
    46 # file through ldd(1).  As objects within a proto area are built to exist in a
       
    47 # base system, standard use of ldd(1) will bind any objects to dependencies
       
    48 # that exist in the base system.  It is frequently the case that newer objects
       
    49 # exist in the proto area that are required to satisfy other objects
       
    50 # dependencies, and without using these newer objects an ldd(1) will produce
       
    51 # misleading error messages.  To compensate for this, the -d option (or the
       
    52 # existence of the CODEMSG_WS/ROOT environment variables) cause the creation of
       
    53 # alternative dependency mappings via crle(1) configuration files that establish
       
    54 # any proto shared objects as alternatives to their base system location.  Thus
       
    55 # ldd(1) can be executed against these configuration files so that objects in a
       
    56 # proto area bind to their dependencies in the same proto area.
       
    57 
       
    58 
       
    59 # Define all global variables (required for strict)
       
    60 use vars  qw($SkipDirs $SkipFiles $SkipTextrelFiles);
       
    61 use vars  qw($SkipUndefDirs $SkipUndefFiles $SkipUnusedDirs $SkipUnusedFiles);
       
    62 use vars  qw($SkipStripDirs $SkipNoStripFiles $SkipStabFiles $SkipNoExStkFiles);
       
    63 use vars  qw($UnusedNoise $Prog $Mach $Isalist $Env $Ena64 $Tmpdir $Error);
       
    64 use vars  qw($UnusedFiles $UnusedPaths $LddNoU $Crle32 $Crle64 $Conf32 $Conf64);
       
    65 use vars  qw($SkipInterps $OldDeps %opt);
       
    66 
       
    67 use strict;
       
    68 
       
    69 
       
    70 # Define any directories we should skip completely.
       
    71 $SkipDirs = qr{ 
       
    72 	etc/lib |			# special - used for partial statics
       
    73 	usr/lib/devfsadm |		# 4382889
       
    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 	ld\.so\.1 |			# confusing but correct dependencies
       
    83 	lddstub |			# lddstub has no dependencies
       
    84 	libmakestate\.so\.1 |		# temporary; delivered by compiler group
       
    85 	libm\.so\.1 |			# temporary; delivered by compiler group
       
    86 	libm\.so\.2 |			# temporary; delivered by compiler group
       
    87 	geniconvtbl\.so |		# 4384329
       
    88 	libssagent\.so\.1 |		# 4328854
       
    89 	libdmi\.so\.1 |			#  "  "
       
    90 	libdmici\.so\.1 |		#  "  "
       
    91 	libdmimi\.so\.1 |		#  "  "
       
    92 	libpsvcplugin_psr\.so\.1 |	# 4385799
       
    93 	libpsvcpolicy_psr\.so\.1 |	#  "  "
       
    94 	libpsvcpolicy\.so\.1 |		#  "  "
       
    95 	picl_slm\.so |			#  "  "
       
    96 	libcrypto_extra\.so\.0\.9\.7 |	# OpenSSL SUNWcry filter lib
       
    97 	libssl_extra\.so\.0\.9\.7 |	# OpenSSL SUNWcry filter lib
       
    98 	fcpackage\.so |			# circular dependency on fcthread.so
       
    99 	grub
       
   100 	)$
       
   101 }x;
       
   102 
       
   103 # Define any files that are allowed text relocations.
       
   104 $SkipTextrelFiles = qr{ ^(?:
       
   105 	unix |				# kernel models are non-pic
       
   106 	mdb				# relocations against __RTC (dbx)
       
   107 	)$
       
   108 }x;
       
   109 
       
   110 # Define any files that are allowed undefined references.
       
   111 $SkipUndefDirs = qr{
       
   112 	usr/lib/inet/ppp/ |		# pppd plugins have callbacks
       
   113 	usr/lib/libp/ |			# libc.so.1 requires _mcount
       
   114 	usr/lib/vold/ |			# vold dependencies have callbacks
       
   115 	usr/lib/rmmount |		# rmmount actions have callbacks
       
   116 	/lib/mdb/ |			# mdb modules have callbacks
       
   117 	/lib/fm/fmd/plugins/ |		# fmd modules have callbacks
       
   118 	/lib/fm/fmd/schemes/ 		# fmd schemes have callbacks
       
   119 }x;
       
   120 
       
   121 $SkipUndefFiles = qr{ ^(?:
       
   122 	libthread_db\.so\.0 |		# callbacks to proc service interface
       
   123 	libthread_db\.so\.1 |		#  "	"	"	"
       
   124 	librtld_db\.so\.1 |		#  "	"	"	"
       
   125 	libc_db\.so\.1 |		#  "	"	"	"
       
   126 	libldstab\.so\.1 |		# link-edit support libraries have
       
   127 	libld\.so\.3 |			# callback to the link-editors
       
   128 	libld\.so\.2 |			#  "	"	"	"
       
   129 	liblddbg\.so\.4 |		#  "	"	"	"
       
   130 	librtld\.so\.1 |		#  "	"	"	"
       
   131 	libnisdb\.so\.2 |		# C++
       
   132 	libsvm\.so\.1 |			# libspmicommon.so.1 lacking
       
   133 	libwanboot\.so\.1 |		# libcrypto.a and libssl.a
       
   134 	libwrap\.so\.1\.0 |		# uses symbols provided by application
       
   135 	fcthread\.so |			# uses symbols provided by application
       
   136 	fn\.so\.2 |			# callback to automount
       
   137 	preen_md\.so\.1 |		# callback to driver
       
   138 	libike\.so\.1 |			# callbacks to in.iked for IKE policy
       
   139 	devfsadmd_mod\.so |		# sysevent module callback to syseventd
       
   140 	sysevent_conf_mod\.so |		# sysevent module callback to syseventd
       
   141 	sysevent_reg_mod\.so		# sysevent module callback to syseventd
       
   142 	)$
       
   143 }x;
       
   144 
       
   145 # Define any files that have unused dependencies.
       
   146 $SkipUnusedDirs = qr{
       
   147 	lib/picl/plugins/ |		# require devtree dependencies
       
   148 	/lib/libp			# profile libc makes libm an unused
       
   149 }x;					#	dependency of standard libc
       
   150 
       
   151 $SkipUnusedFiles = qr{ ^(?:
       
   152 	devfsadm |			# 4382889
       
   153 	disks |				#  "  "
       
   154 	tapes |				#  "  "
       
   155 	ports |				#  "  "
       
   156 	audlinks |			#  "  "
       
   157 	devlinks |			#  "  "
       
   158 	drvconfig |			#  "  "
       
   159 	ntptrace |			# on intel doesn't need libmd5
       
   160 	rmmount |			# 4418770, volmgt dependency is required
       
   161 					#	to compensate for SunPCi.
       
   162 	ocfserv |			# libsched unreference by libjvm,
       
   163 	poold |				#	see 4952319. 
       
   164 	libc\.so\.1\.9 |		# 4lib/libc versions have private
       
   165 	libc\.so\.2\.9			#	copies of stuff from libc.
       
   166 	)$
       
   167 }x;
       
   168 
       
   169 # Define any files that can be stripped.
       
   170 $SkipStripDirs = qr{
       
   171 	/abi/
       
   172 }x;
       
   173 
       
   174 # Define any files that must not be stripped.
       
   175 $SkipNoStripFiles = qr{ ^(?:
       
   176 	adb |
       
   177 	mdb |
       
   178 	unix
       
   179 	)$
       
   180 }x;
       
   181 
       
   182 # Define any files that should contain debugging information.
       
   183 $SkipStabFiles = qr{ ^(?:
       
   184 	abi_.* |
       
   185 	interceptors\.so\.1 |
       
   186 	unix
       
   187 	)$
       
   188 }x;
       
   189 
       
   190 # Define any files that don't require a non-executable stack definition.
       
   191 $SkipNoExStkFiles = qr{ ^(?:
       
   192 	forth |
       
   193 	unix |
       
   194 	multiboot
       
   195 	)$
       
   196 }x;
       
   197 
       
   198 # Define any files that should only have unused (ldd -u) processing.
       
   199 $UnusedPaths = qr{
       
   200 	ucb/shutdown			# libucb interposes on libc and makes
       
   201 					# dependencies on libc seem unnecessary
       
   202 }x;
       
   203 
       
   204 $UnusedFiles = qr{ ^(?:
       
   205 	rpc\.nisd			# CCNEEDED makes pthread unreferenced
       
   206 	)$
       
   207 }x;
       
   208 
       
   209 # Define unused dependencies we should ignore.
       
   210 # libCrun has a unnecessary dependency on libw, and libmapmalloc is often
       
   211 # defined to interpose on libc but isn't used by the application itself.
       
   212 # Threads dependencies look unused if libc is bound first.
       
   213 $UnusedNoise = qr{
       
   214 	libw\.so\.1;\ unused |
       
   215 	unused\ object=.*libw\.so\.1 |
       
   216 	libthread\.so\.1;\ unused |
       
   217 	libpthread\.so\.1;\ unused |
       
   218 	unused\ object=.*libpthread\.so\.1 |
       
   219 	libnsl\.so\.1;\ unused\ dependency\ of\ .*libxslt\.so\.1 |
       
   220 	libdl\.so\.1;\ unused\ dependency\ of\ .*libspmicommon\.so\.1 |
       
   221 	libdl\.so\.1;\ unused\ dependency\ of\ .*libCrun\.so\.1 |
       
   222 	libfru\.so\.1;\ unused\ object=.*libdl\.so\.1 |
       
   223 	libfrupicl\.so\.1;\ unused\ object=.*libdl\.so\.1 |
       
   224 	libmapmalloc\.so\.1;\ unused
       
   225 }x;
       
   226 
       
   227 
       
   228 # Define interpreters we should ignore.
       
   229 $SkipInterps = qr{
       
   230 	misc/krtld |
       
   231 	misc/amd64/krtld |
       
   232 	misc/sparcv9/krtld
       
   233 }x;
       
   234 
       
   235 # Catch libintl and libw, although ld(1) will bind to these and thus determine
       
   236 # they're needed, their content was moved into libc as of on297 build 7.
       
   237 # libthread and libpthread were completely moved into libc as of on10 build 53.
       
   238 # Also, catch libdl, whose content was moved into libc as of on10 build 49.
       
   239 $OldDeps = qr{ ^(?:
       
   240 	libintl\.so\.1 |
       
   241 	libw\.so\.1 |
       
   242 	libthread\.so\.1 |
       
   243 	libpthread\.so\.1 |
       
   244 	libdl\.so\.1
       
   245 	)$
       
   246 }x;
       
   247 
       
   248 use Getopt::Std;
       
   249 
       
   250 # -----------------------------------------------------------------------------
       
   251 
       
   252 # Reliably compare two OS revisions.  Arguments are <ver1> <op> <ver2>.
       
   253 # <op> is the string form of a normal numeric comparison operator.
       
   254 sub cmp_os_ver {
       
   255 	my @ver1 = split(/\./, $_[0]);
       
   256 	my $op = $_[1];
       
   257 	my @ver2 = split(/\./, $_[2]);
       
   258 
       
   259 	push @ver2, ("0") x $#ver1 - $#ver2;
       
   260 	push @ver1, ("0") x $#ver2 - $#ver1;
       
   261 
       
   262 	my $diff = 0;
       
   263 	while (@ver1 || @ver2) {
       
   264 		if (($diff = shift(@ver1) - shift(@ver2)) != 0) {
       
   265 			last;
       
   266 		}
       
   267 	}
       
   268 	return (eval "$diff $op 0" ? 1 : 0);
       
   269 }
       
   270 
       
   271 # Establish a program name for any error diagnostics.
       
   272 chomp($Prog = `basename $0`);
       
   273 
       
   274 # Determine what machinery is available.
       
   275 $Mach = `uname -p`;
       
   276 $Isalist = `isalist`;
       
   277 $Env = "";
       
   278 if ($Mach =~ /sparc/) {
       
   279 	if ($Isalist =~ /sparcv9/) {
       
   280 		$Ena64 = "ok";
       
   281 	}
       
   282 } elsif ($Mach =~ /i386/) {
       
   283 	if ($Isalist =~ /amd64/) {
       
   284 		$Ena64 = "ok";
       
   285 	}
       
   286 }
       
   287 
       
   288 # Check that we have arguments.
       
   289 if ((getopts('ad:imos', \%opt) == 0) || ($#ARGV == -1)) {
       
   290 	print "usage: $Prog [-a] [-d depdir] [-m] [-o] [-s] file | dir, ...\n";
       
   291 	print "\t[-a]\t\tprocess all files (ignore any exception lists)\n";
       
   292 	print "\t[-d dir]\testablish dependencies from under directory\n";
       
   293 	print "\t[-i]\t\tproduce dynamic table entry information\n";
       
   294 	print "\t[-m]\t\tprocess mcs(1) comments\n";
       
   295 	print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
       
   296 	print "\t[-s]\t\tprocess .stab and .symtab entries\n";
       
   297 	exit 1;
       
   298 } else {
       
   299 	my($Proto);
       
   300 
       
   301 	if ($opt{d}) {
       
   302 		# User specified dependency directory - make sure it exists.
       
   303 		if (! -d $opt{d}) {
       
   304 			print "$Prog: $opt{d} is not a directory\n";
       
   305 			exit 1;
       
   306 		}
       
   307 		$Proto = $opt{d};
       
   308 
       
   309 	} elsif ($ENV{CODEMGR_WS}) {
       
   310 		my($Root);
       
   311 
       
   312 		# Without a user specified dependency directory see if we're
       
   313 		# part of a codemanager workspace and if a proto area exists.
       
   314 		if (($Root = $ENV{ROOT}) && (-d $Root)) {
       
   315 			$Proto = $Root;
       
   316 		}
       
   317 	}
       
   318 
       
   319 	if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir)) {
       
   320 		$Tmpdir = "/tmp";
       
   321 	}
       
   322 
       
   323 	# Look for dependencies under $Proto.
       
   324 	if ($Proto) {
       
   325 		# To support alternative dependency mapping we'll need ldd(1)'s
       
   326 		# -e option.  This is relatively new (s81_30), so make sure
       
   327 		# ldd(1)is capable before gathering any dependency information.
       
   328 		if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) {
       
   329 			print "ldd: does not support -e, unable to ";
       
   330 			print "create alternative dependency mappingings.\n";
       
   331 			print "ldd: option added under 4390308 (s81_30).\n\n";
       
   332 		} else {
       
   333 			# Gather dependencies and construct a alternative
       
   334 			# dependency mapping via a crle(1) configuration file.
       
   335 			GetDeps($Proto, "/");
       
   336 			GenConf();
       
   337 		}
       
   338 	}
       
   339 
       
   340 	# To support unreferenced dependency detection we'll need ldd(1)'s -U
       
   341 	# option.  This is relatively new (4638070), and if not available we
       
   342 	# can still fall back to -u.  Even with this option, don't use -U with
       
   343 	# releases prior to 5.10 as the cleanup for -U use only got integrated
       
   344 	# into 5.10 under 4642023.  Note, that nightly doesn't typically set a
       
   345 	# RELEASE from the standard <env> files.  Users who wish to disable use
       
   346 	# of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file
       
   347 	# if using nightly, or otherwise establish it in their environment.
       
   348 	if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) {
       
   349 		$LddNoU = 1;
       
   350 	} else {
       
   351 		my($Release);
       
   352 
       
   353 		if (($Release = $ENV{RELEASE}) &&
       
   354 		    (cmp_os_ver($Release, "<", "5.10"))) {
       
   355 			$LddNoU = 1;
       
   356 		} else {
       
   357 			$LddNoU = 0;
       
   358 		}
       
   359 	}
       
   360 
       
   361 	# For each argument determine if we're dealing with a file or directory.
       
   362 	foreach my $Arg (@ARGV) {
       
   363 		# Ignore symbolic links.
       
   364 		if (-l $Arg) {
       
   365 			next;
       
   366 		}
       
   367 
       
   368 		if (!stat($Arg)) {
       
   369 			next;
       
   370 		}
       
   371 
       
   372 		# Process simple files.
       
   373 		if (-f _) {
       
   374 			my($RelPath) = $Arg;
       
   375 			my($File) = $Arg;
       
   376 			my($Secure) = 0;
       
   377 
       
   378 			$RelPath =~ s!^.*/!./!;
       
   379 			$File =~ s!^.*/!!;
       
   380 
       
   381 			if (-u _ || -g _) {
       
   382 				$Secure = 1;
       
   383 			}
       
   384 
       
   385 			ProcFile($Arg, $RelPath, $File, $Secure);
       
   386 			next;
       
   387 		}
       
   388 		# Process directories.
       
   389 		if (-d _) {
       
   390 			ProcDir($Arg, ".");
       
   391 			next;
       
   392 		}
       
   393 
       
   394 		print "$Arg is not a file or directory\n";
       
   395 		$Error = 1;
       
   396 	}
       
   397 
       
   398 	# Cleanup
       
   399 	CleanUp();
       
   400 }
       
   401 
       
   402 $Error = 0;
       
   403 
       
   404 # Clean up and temporary files.
       
   405 sub CleanUp {
       
   406 	if ($Crle64) {
       
   407 		unlink $Crle64;
       
   408 	}
       
   409 	if ($Conf64) {
       
   410 		unlink $Conf64;
       
   411 	}
       
   412 	if ($Crle32) {
       
   413 		unlink $Crle32;
       
   414 	}
       
   415 	if ($Conf32) {
       
   416 		unlink $Conf32;
       
   417 	}
       
   418 }
       
   419 
       
   420 # Create an output message, either a one-liner (under -o) or preceded by the
       
   421 # files relative pathname as a title.
       
   422 sub OutMsg {
       
   423 	my($Ttl, $Path, $Msg) = @_;
       
   424 
       
   425 	if ($opt{o}) {
       
   426 		$Msg =~ s/^[ \t]*//;
       
   427 		print "$Path: $Msg\n";
       
   428 	} else {
       
   429 		if ($Ttl eq 0) {
       
   430 			print "==== $Path ====\n";
       
   431 		}
       
   432 		print "$Msg\n";
       
   433 	}
       
   434 }
       
   435 
       
   436 # Determine whether this a ELF dynamic object and if so investigate its runtime
       
   437 # attributes.
       
   438 sub ProcFile {
       
   439 	my($FullPath, $RelPath, $File, $Secure) = @_;
       
   440 	my(@Elf, @Ldd, $Dyn, $Intp, $Dll, $Ttl, $Sym, $Interp, $Stack);
       
   441 	my($Sun, $Relsz, $Pltsz, $Uns, $Tex, $Stab, $Strip, $Lddopt);
       
   442 	my($Val, $Header, $SkipLdd, $IsX86, $RWX);
       
   443 
       
   444 	# Ignore symbolic links.
       
   445 	if (-l $FullPath) {
       
   446 		return;
       
   447 	}
       
   448 
       
   449 	$Ttl = 0;
       
   450 	@Ldd = 0;
       
   451 
       
   452 	# Determine whether we have access to inspect the file.
       
   453 	if (!(-r $FullPath)) {
       
   454 		OutMsg($Ttl++, $RelPath,
       
   455 		    "\tunable to inspect file: permission denied");
       
   456 		return;
       
   457 	}
       
   458 
       
   459 	# Determine if this is a file we don't care about.
       
   460 	if (!$opt{a}) {
       
   461 		if ($File =~ $SkipFiles) {
       
   462 			return;
       
   463 		}
       
   464 	}
       
   465 
       
   466 	# Determine whether we have a executable (static or dynamic) or a
       
   467 	# shared object.
       
   468 	@Elf = split(/\n/, `elfdump -epdic $FullPath 2>&1`);
       
   469 
       
   470 	$Dyn = $Intp = $Dll = $Stack = $IsX86 = $RWX = 0;
       
   471 	$Interp = 1;
       
   472 	$Header = 'None';
       
   473 	foreach my $Line (@Elf) {
       
   474 		# If we have an invalid file type (which we can tell from the
       
   475 		# first line), or we're processing an archive, bail.
       
   476 		if ($Header eq 'None') {
       
   477 			if (($Line =~ /invalid file/) ||
       
   478 			    ($Line =~ /$FullPath(.*):/)) {
       
   479 				return;
       
   480 			}
       
   481 		}
       
   482 
       
   483 		if ($Line =~ /^ELF Header/) {
       
   484 			$Header = 'Ehdr';
       
   485 
       
   486 		} elsif ($Line =~ /^Program Header/) {
       
   487 			$Header = 'Phdr';
       
   488 			$RWX = 0;
       
   489 
       
   490 		} elsif ($Line =~ /^Interpreter/) {
       
   491 			$Header = 'Intp';
       
   492 
       
   493 		} elsif ($Line =~ /^Dynamic Section/) {
       
   494 			# A dynamic section indicates we're a dynamic object
       
   495 			# (this makes sure we don't check static executables).
       
   496 			$Dyn = 1;
       
   497 
       
   498 		} elsif (($Header eq 'Ehdr') && ($Line =~ /e_type:/)) {
       
   499 			# The e_type field indicates whether this file is a
       
   500 			# shared object (ET_DYN) or an executable (ET_EXEC).
       
   501 			if ($Line =~ /ET_DYN/) {
       
   502 				$Dll = 1;
       
   503 			} elsif ($Line !~ /ET_EXEC/) {
       
   504 				return;
       
   505 			}
       
   506 		} elsif (($Header eq 'Ehdr') && ($Line =~ /ei_class:/)) {
       
   507 			# If we encounter a 64-bit object, but we're not running
       
   508 			# on a 64-bit system, suppress calling ldd(1).
       
   509 			if (($Line =~ /ELFCLASS64/) && !$Ena64) {
       
   510 				$SkipLdd = 1;
       
   511 			}
       
   512 		} elsif (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) {
       
   513 			# If it's a X86 object, we need to enforce RW- data.
       
   514 			if (($Line =~ /(EM_AMD64|EM_386)/)) {
       
   515 				$IsX86 = 1;
       
   516 			}
       
   517 		} elsif (($Header eq 'Phdr') &&
       
   518 		    ($Line =~ /\[ PF_X  PF_W  PF_R \]/)) {
       
   519 			# RWX segment seen.
       
   520 			$RWX = 1;
       
   521 
       
   522 		} elsif (($Header eq 'Phdr') &&
       
   523 		    ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) {
       
   524 			# Seen an RWX PT_LOAD segment.
       
   525 			if ($File !~ $SkipNoExStkFiles) {
       
   526 				OutMsg($Ttl++, $RelPath,
       
   527 				    "\tapplication requires non-executable " .
       
   528 				    "data\t<no -Mmapfile_noexdata?>");
       
   529 			}
       
   530 
       
   531 		} elsif (($Header eq 'Phdr') &&
       
   532 		    ($Line =~ /\[ PT_SUNWSTACK \]/)) {
       
   533 			# This object defines a non-executable stack.
       
   534 			$Stack = 1;
       
   535 
       
   536 		} elsif (($Header eq 'Intp') && !$opt{a} &&
       
   537 		    ($Line =~ $SkipInterps)) {
       
   538 			# This object defines an interpretor we should skip.
       
   539 			$Interp = 0;
       
   540 		}
       
   541 	}
       
   542 
       
   543 	# Determine whether this ELF executable or shared object has a
       
   544 	# conforming mcs(1) comment section.  If the correct $(POST_PROCESS)
       
   545 	# macros are used, only a 3 or 4 line .comment section should exist
       
   546 	# containing one or two "@(#)SunOS" identifying comments (one comment
       
   547 	# for a non-debug build, and two for a debug build). The results of
       
   548 	# the following split should be three or four lines, the last empty
       
   549 	# line being discarded by the split.
       
   550 	if ($opt{m}) {
       
   551 		my(@Mcs, $Con, $Dev);
       
   552 
       
   553 		@Mcs = split(/\n/, `mcs -p $FullPath 2>&1`);
       
   554 
       
   555 		$Con = $Dev = $Val = 0;
       
   556 		foreach my $Line (@Mcs) {
       
   557 			$Val++;
       
   558 
       
   559 			if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) {
       
   560 				$Con = 1;
       
   561 				last;
       
   562 			}
       
   563 			if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) {
       
   564 				$Dev = 1;
       
   565 				next;
       
   566 			}
       
   567 			if (($Dev == 0) && ($Val == 4)) {
       
   568 				$Con = 1;
       
   569 				last;
       
   570 			}
       
   571 			if (($Dev == 1) && ($Val == 5)) {
       
   572 				$Con = 1;
       
   573 				last;
       
   574 			}
       
   575 		}
       
   576 		if ($opt{m} && ($Con == 1)) {
       
   577 			OutMsg($Ttl++, $RelPath,
       
   578 			    "\tnon-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>");
       
   579 		}
       
   580 	}
       
   581 
       
   582 	# Applications should contain a non-executable stack definition.
       
   583 	if (($Dll == 0) && ($Stack == 0)) {
       
   584 		if (!$opt{a}) {
       
   585 			if ($File =~ $SkipNoExStkFiles) {
       
   586 				goto DYN;
       
   587 			}
       
   588 		}
       
   589 		OutMsg($Ttl++, $RelPath,
       
   590 		    "\tapplication requires non-executable stack\t<no -Mmapfile_noexstk?>");
       
   591 	}
       
   592 
       
   593 DYN:
       
   594 	# Having caught any static executables in the mcs(1) check and non-
       
   595 	# executable stack definition check, continue with dynamic objects
       
   596 	# from now on.
       
   597 	if ($Dyn eq 0) {
       
   598 		return;
       
   599 	}
       
   600 
       
   601 	# Only use ldd unless we've encountered an interpreter that should
       
   602 	# ne skipped.
       
   603 	if (!$SkipLdd && $Interp) {
       
   604 		if ($Secure) {
       
   605 			# The execution of a secure application over an nfs file
       
   606 			# system mounted nosuid will result in warning messages
       
   607 			# being sent to /var/adm/messages.  As this type of
       
   608 			# environment can occur with root builds, move the file
       
   609 			# being investigated to a safe place first.  In addition
       
   610 			# remove its secure permission so that it can be
       
   611 			# influenced by any alternative dependency mappings.
       
   612 	
       
   613 			my($TmpPath) = "$Tmpdir/$File";
       
   614 
       
   615 			system('cp', $FullPath, $TmpPath);
       
   616 			chmod 0777, $TmpPath;
       
   617 			$FullPath = $TmpPath;
       
   618 		}
       
   619 
       
   620 		# Use ldd(1) to determine the objects relocatability and use.
       
   621 		# By default look for all unreferenced dependencies.  However,
       
   622 		# some objects have legitimate dependencies that they do not
       
   623 		# reference.
       
   624 		if ($LddNoU || ($File =~ $UnusedFiles) ||
       
   625 		    ($RelPath =~ $UnusedPaths)) {
       
   626 			$Lddopt = "-ru";
       
   627 		} else {
       
   628 			$Lddopt = "-rU";
       
   629 		}
       
   630 		@Ldd = split(/\n/, `ldd $Lddopt $Env $FullPath 2>&1`);
       
   631 		if ($Secure) {
       
   632 			unlink $FullPath;
       
   633 		}
       
   634 	}
       
   635 
       
   636 	$Val = 0;
       
   637 	$Sym = 5;
       
   638 	$Uns = 1;
       
   639 
       
   640 LDD:	foreach my $Line (@Ldd) {
       
   641 
       
   642 		if ($Val == 0) {
       
   643 			$Val = 1;
       
   644 			# Make sure ldd(1) worked.  One possible failure is that
       
   645 			# this is an old ldd(1) prior to -e addition (4390308).
       
   646 			if ($Line =~ /usage:/) {
       
   647 				$Line =~ s/$/\t<old ldd(1)?>/;
       
   648 				OutMsg($Ttl++, $RelPath, $Line);
       
   649 				last;
       
   650 			} elsif ($Line =~ /execution failed/) {
       
   651 				OutMsg($Ttl++, $RelPath, $Line);
       
   652 				last;
       
   653 			}
       
   654 
       
   655 			# It's possible this binary can't be executed, ie. we've
       
   656 			# found a sparc binary while running on an intel system,
       
   657 			# or a sparcv9 binary on a sparcv7/8 system.
       
   658 			if ($Line =~ /wrong class/) {
       
   659 				OutMsg($Ttl++, $RelPath,
       
   660 				    "\thas wrong class or data encoding");
       
   661 				next;
       
   662 			}
       
   663 
       
   664 			# Historically, ldd(1) likes executable objects to have
       
   665 			# their execute bit set.  Note that this test isn't
       
   666 			# applied unless the -a option is in effect, as any
       
   667 			# non-executable files are skipped by default to reduce
       
   668 			# the cost of running this script.
       
   669 			if ($Line =~ /not executable/) {
       
   670 				OutMsg($Ttl++, $RelPath,
       
   671 				    "\tis not executable");
       
   672 				next;
       
   673 			}
       
   674 		}
       
   675 
       
   676 		# Look for "file" or "versions" that aren't found.  Note that
       
   677 		# these lines will occur before we find any symbol referencing
       
   678 		# errors.
       
   679 		if (($Sym == 5) && ($Line =~ /not found\)/)) {
       
   680 			if ($Line =~ /file not found\)/) {
       
   681 				$Line =~ s/$/\t<no -zdefs?>/;
       
   682 			}
       
   683 			OutMsg($Ttl++, $RelPath, $Line);
       
   684 			next;
       
   685 		}
       
   686 		# Look for relocations whose symbols can't be found.  Note, we
       
   687 		# only print out the first 5 relocations for any file as this
       
   688 		# output can be excessive.
       
   689 		if ($Sym && ($Line =~ /symbol not found/)) {
       
   690 			# Determine if this file is allowed undefined
       
   691 			# references.
       
   692 			if ($Sym == 5) {
       
   693 				if (!$opt{a}) {
       
   694 					if ($RelPath =~ $SkipUndefDirs) {
       
   695 						$Sym = 0;
       
   696 						next LDD;
       
   697 					}
       
   698 					if ($File =~ $SkipUndefFiles) {
       
   699 						$Sym = 0;
       
   700 						next LDD;
       
   701 					}
       
   702 				}
       
   703 			}
       
   704 			if ($Sym-- == 1) {
       
   705 				if (!$opt{o}) {
       
   706 					OutMsg($Ttl++, $RelPath,
       
   707 					    "\tcontinued ...");
       
   708 				}
       
   709 				next;
       
   710 			}
       
   711 			# Just print the symbol name.
       
   712 			$Line =~ s/$/\t<no -zdefs?>/;
       
   713 			OutMsg($Ttl++, $RelPath, $Line);
       
   714 			next;
       
   715 		}
       
   716 		# Look for any unused dependencies.
       
   717 		if ($Uns && ($Line =~ /unused/)) {
       
   718 			if (!$opt{a}) {
       
   719 				if ($RelPath =~ $SkipUnusedDirs) {
       
   720 					$Uns = 0;
       
   721 					next LDD;
       
   722 				}
       
   723 				if ($File =~ $SkipUnusedFiles) {
       
   724 					$Uns = 0;
       
   725 					next LDD;
       
   726 				}
       
   727 
       
   728 				# Remove any noise.
       
   729 				if ($Line =~ $UnusedNoise) {
       
   730 					$Uns = 0;
       
   731 					next LDD;
       
   732 				}
       
   733 			}
       
   734 			if ($Secure) {
       
   735 				$Line =~ s!$Tmpdir/!!;
       
   736 			}
       
   737 			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/;
       
   738 			OutMsg($Ttl++, $RelPath, $Line);
       
   739 			next;
       
   740 		}
       
   741 	}
       
   742 
       
   743 	# Reuse the elfdump(1) data to investigate additional dynamic linking
       
   744 	# information.
       
   745 
       
   746 	$Sun = $Relsz = $Pltsz = $Dyn = $Stab = 0;
       
   747 	$Tex = $Strip = 1;
       
   748 
       
   749 	$Header = 'None';
       
   750 ELF:	foreach my $Line (@Elf) {
       
   751 		# We're only interested in the section headers and the dynamic
       
   752 		# section.
       
   753 		if ($Line =~ /^Section Header/) {
       
   754 			$Header = 'Shdr';
       
   755 
       
   756 			if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) {
       
   757 				# This object has a combined relocation section.
       
   758 				$Sun = 1;
       
   759 
       
   760 			} elsif (($Stab == 0) && ($Line =~ /\.stab/)) {
       
   761 				# This object contain .stabs sections
       
   762 				$Stab = 1;
       
   763 			}
       
   764 
       
   765 			if (($Strip == 1) && ($Line =~ /\.symtab/)) {
       
   766 				# This object contains a complete symbol table.
       
   767 				$Strip = 0;
       
   768 			}
       
   769 			next;
       
   770 
       
   771 		} elsif ($Line =~ /^Dynamic Section/) {
       
   772 			$Header = 'Dyn';
       
   773 			next;
       
   774 		} elsif ($Header ne 'Dyn') {
       
   775 			next;
       
   776 		}
       
   777 
       
   778 		# Does this object contain text relocations.
       
   779 		if ($Tex && ($Line =~ /TEXTREL/)) {
       
   780 			# Determine if this file is allowed text relocations.
       
   781 			if (!$opt{a}) {
       
   782 				if ($File =~ $SkipTextrelFiles) {
       
   783 					$Tex = 0;
       
   784 					next ELF;
       
   785 				}
       
   786 			}
       
   787 			OutMsg($Ttl++, $RelPath,
       
   788 			    "\tTEXTREL .dynamic tag\t\t\t<no -Kpic?>");
       
   789 			$Tex = 0;
       
   790 			next;
       
   791 		}
       
   792 
       
   793 		# Does this file have any relocation sections (there are a few
       
   794 		# psr libraries with no relocations at all, thus a .SUNW_reloc
       
   795 		# section won't exist either).
       
   796 		if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) {
       
   797 			$Relsz = hex((split(' ', $Line))[2]);
       
   798 			next;
       
   799 		}
       
   800 
       
   801 		# Does this file have any plt relocations.  If the plt size is
       
   802 		# equivalent to the total relocation size then we don't have
       
   803 		# any relocations suitable for combining into a .SUNW_reloc
       
   804 		# section.
       
   805 		if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) {
       
   806 			$Pltsz = hex((split(' ', $Line))[2]);
       
   807 			next;
       
   808 		}
       
   809 
       
   810 		# Under the -i (information) option print out any useful dynamic
       
   811 		# entries.
       
   812 		# Does this object have any dependencies.
       
   813 		if ($opt{i} && ($Line =~ /NEEDED/)) {
       
   814 			my($Need) = (split(' ', $Line))[3];
       
   815 
       
   816 			# Catch any old (unnecessary) dependencies.
       
   817 			if ($Need =~ $OldDeps) {
       
   818 				OutMsg($Ttl++, $RelPath,
       
   819 				    "\tNEEDED=$Need\t<dependency no longer necessary>");
       
   820 			} else { 
       
   821 				OutMsg($Ttl++, $RelPath, "\tNEEDED=$Need");
       
   822 			}
       
   823 			next;
       
   824 		}
       
   825 
       
   826 		# Does this object specify a runpath.
       
   827 		if ($opt{i} && ($Line =~ /RPATH/)) {
       
   828 			my($Rpath) = (split(' ', $Line))[3];
       
   829 			OutMsg($Ttl++, $RelPath, "\tRPATH=$Rpath");
       
   830 			next;
       
   831 		}
       
   832 	}
       
   833 
       
   834 	# A shared object, that contains non-plt relocations, should have a
       
   835 	# combined relocation section indicating it was built with -z combreloc.
       
   836 	if ($Dll && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) {
       
   837 		OutMsg($Ttl++, $RelPath,
       
   838 		    "\tSUNW_reloc section missing\t\t<no -zcombreloc?>");
       
   839 	}
       
   840 
       
   841 	# No objects released to a customer should have any .stabs sections
       
   842 	# remaining, they should be stripped.
       
   843 	if ($opt{s} && $Stab) {
       
   844 		if (!$opt{a}) {
       
   845 			if ($File =~ $SkipStabFiles) {
       
   846 				goto DONESTAB;
       
   847 			}
       
   848 		}
       
   849 		OutMsg($Ttl++, $RelPath,
       
   850 		    "\tdebugging sections should be deleted\t<no -s or strip -x?>");
       
   851 	}
       
   852 
       
   853 DONESTAB:
       
   854 
       
   855 	# Shared objects should have a full symbol table to provide complete
       
   856 	# debugging stack traces.
       
   857 	if ($opt{s} && $Dll && $Strip) {
       
   858 		if (!$opt{a}) {
       
   859 			if ($RelPath =~ $SkipStripDirs) {
       
   860 				goto DONESTRIP;
       
   861 			}
       
   862 		}
       
   863 		OutMsg($Ttl++, $RelPath,
       
   864 		    "\tsymbol table should not be stripped\t<remove -s?>");
       
   865 	}
       
   866 
       
   867 	# No other dynamic object should have a .symtab symbol table.
       
   868 	if ($opt{s} && ($Dll == 0) && ($Strip == 0)) {
       
   869 		if (!$opt{a}) {
       
   870 			if ($File =~ $SkipNoStripFiles) {
       
   871 				goto DONESTRIP;
       
   872 			}
       
   873 		}
       
   874 		OutMsg($Ttl++, $RelPath,
       
   875 		    "\tsymbol table should be stripped\t<no -s?>");
       
   876 	}
       
   877 
       
   878 DONESTRIP:
       
   879 
       
   880 }
       
   881 
       
   882 
       
   883 sub ProcDir {
       
   884 	my($FullDir, $RelDir) = @_;
       
   885 	my($NewFull, $NewRel);
       
   886 
       
   887 	# Determine if this is a directory we don't care about.
       
   888 	if (!$opt{a}) {
       
   889 		if ($RelDir =~ $SkipDirs) {
       
   890 			return;
       
   891 		}
       
   892 	}
       
   893 
       
   894 	# Open the directory and read each entry, omit files starting with "."
       
   895 	if (opendir(DIR, $FullDir)) {
       
   896 		foreach my $Entry (readdir(DIR)) {
       
   897 			if ($Entry =~ /^\./) {
       
   898 				next;
       
   899 			}
       
   900 			$NewFull = "$FullDir/$Entry";
       
   901 
       
   902 			# Ignore symlinks.
       
   903 			if (-l $NewFull) {
       
   904 				next;
       
   905 			}
       
   906 			if (!stat($NewFull)) {
       
   907 				next;
       
   908 			}
       
   909 			$NewRel = "$RelDir/$Entry";
       
   910 
       
   911 			# Descend into and process any directories.
       
   912 			if (-d _) {
       
   913 				ProcDir($NewFull, $NewRel);
       
   914 				next;
       
   915 			}
       
   916 
       
   917 			# Typically dynamic objects are executable, so we can
       
   918 			# reduce the overall cost of this script (a lot!) by
       
   919 			# screening out non-executables here, rather than pass
       
   920 			# them to file(1) later.  However, it has been known
       
   921 			# for shared objects to be mistakenly left non-
       
   922 			# executable, so with -a let all files through so that
       
   923 			# this requirement can be verified (see ProcFile()).
       
   924 			if (!$opt{a}) {
       
   925 				if (! -x _) {
       
   926 					next;
       
   927 				}
       
   928 			}
       
   929 
       
   930 			# Process any standard files.
       
   931 			if (-f _) {
       
   932 				my($Secure) = 0;
       
   933 
       
   934 				if (-u _ || -g _) {
       
   935 					$Secure = 1;
       
   936 				}
       
   937 
       
   938 				ProcFile($NewFull, $NewRel, $Entry, $Secure);
       
   939 				next;
       
   940 			}
       
   941 
       
   942 		}
       
   943 		closedir(DIR);
       
   944 	}
       
   945 }
       
   946 
       
   947 # Create a crle(1) script for any 64-bit dependencies we locate.  A runtime
       
   948 # configuration file will be generated to establish alternative dependency
       
   949 # mappings for all these dependencies.
       
   950 
       
   951 sub Entercrle64 {
       
   952 	my($FullDir, $RelDir, $Entry) = @_;
       
   953 
       
   954 	if (!$Crle64) {
       
   955 		# Create and initialize the script if is doesn't already exit.
       
   956 
       
   957 		$Crle64 = "$Tmpdir/$Prog.crle64.$$";
       
   958 		open(CRLE64, "> $Crle64") ||
       
   959 			die "$Prog: open failed: $Crle64: $!";
       
   960 
       
   961 		print CRLE64 "#!/bin/sh\ncrle -64\\\n";
       
   962 	}
       
   963 	print CRLE64 "\t-o $FullDir -a $RelDir/$Entry \\\n";
       
   964 }
       
   965 
       
   966 # Create a crle(1) script for any 32-bit dependencies we locate.  A runtime
       
   967 # configuration file will be generated to establish alternative dependency
       
   968 # mappings for all these dependencies.
       
   969 
       
   970 sub Entercrle32 {
       
   971 	my($FullDir, $RelDir, $Entry) = @_;
       
   972 
       
   973 	if (!$Crle32) {
       
   974 		# Create and initialize the script if is doesn't already exit.
       
   975 
       
   976 		$Crle32 = "$Tmpdir/$Prog.crle32.$$";
       
   977 		open(CRLE32, "> $Crle32") ||
       
   978 			die "$Prog: open failed: $Crle32: $!";
       
   979 
       
   980 		print CRLE32 "#!/bin/sh\ncrle \\\n";
       
   981 	}
       
   982 	print CRLE32 "\t-o $FullDir -a $RelDir/$Entry \\\n";
       
   983 }
       
   984 
       
   985 # Having finished gathering dependencies, complete any crle(1) scripts and
       
   986 # execute them to generate the associated runtime configuration files.  In
       
   987 # addition establish the environment variable required to pass the configuration
       
   988 # files to ldd(1).
       
   989 
       
   990 sub GenConf {
       
   991 	if ($Crle64) {
       
   992 		$Conf64 = "$Tmpdir/$Prog.conf64.$$";
       
   993 		print CRLE64 "\t-c $Conf64\n";
       
   994 
       
   995 		chmod 0755, $Crle64;
       
   996 		close CRLE64;
       
   997 
       
   998 		if (system($Crle64)) {
       
   999 			undef $Conf64;
       
  1000 		}
       
  1001 	}
       
  1002 	if ($Crle32) {
       
  1003 		$Conf32 = "$Tmpdir/$Prog.conf32.$$";
       
  1004 		print CRLE32 "\t-c $Conf32\n";
       
  1005 
       
  1006 		chmod 0755, $Crle32;
       
  1007 		close CRLE32;
       
  1008 
       
  1009 		if (system($Crle32)) {
       
  1010 			undef $Conf32;
       
  1011 		}
       
  1012 	}
       
  1013 
       
  1014 	if ($Crle64 && $Conf64 && $Crle32 && $Conf32) {
       
  1015 		$Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32";
       
  1016 	} elsif ($Crle64 && $Conf64) {
       
  1017 		$Env = "-e LD_FLAGS=config_64=$Conf64";
       
  1018 	} elsif ($Crle32 && $Conf32) {
       
  1019 		$Env = "-e LD_FLAGS=config_32=$Conf32";
       
  1020 	}
       
  1021 }
       
  1022 
       
  1023 # Recurse through a directory hierarchy looking for appropriate dependencies.
       
  1024 
       
  1025 sub GetDeps {
       
  1026 	my($FullDir, $RelDir) = @_;
       
  1027 	my($NewFull);
       
  1028 
       
  1029 	# Open the directory and read each entry, omit files starting with "."
       
  1030 	if (opendir(DIR, $FullDir)) {
       
  1031 		 foreach my $Entry (readdir(DIR)) {
       
  1032 			if ($Entry =~ /^\./) {
       
  1033 				next;
       
  1034 			}
       
  1035 			$NewFull = "$FullDir/$Entry";
       
  1036 
       
  1037 			# We need to follow links so that any dependencies
       
  1038 			# are expressed in all their available forms.
       
  1039 			# Bail on symlinks like 32 -> .
       
  1040 			if (-l $NewFull) {
       
  1041 				if (readlink($NewFull) =~ /^\.$/) {
       
  1042 					next;
       
  1043 				}
       
  1044 			}
       
  1045 			if (!stat($NewFull)) {
       
  1046 				next;
       
  1047 			}
       
  1048 
       
  1049 			# If this is a directory descend into it.
       
  1050 			if (-d _) {
       
  1051 				my($NewRel);
       
  1052 				
       
  1053 				if ($RelDir =~ /^\/$/) {
       
  1054 					$NewRel = "$RelDir$Entry";
       
  1055 				} else {
       
  1056 					$NewRel = "$RelDir/$Entry";
       
  1057 				}
       
  1058 
       
  1059 				GetDeps($NewFull, $NewRel);
       
  1060 				next;
       
  1061 			}
       
  1062 
       
  1063 			# If this is a regular file determine if its a
       
  1064 			# valid ELF dependency.
       
  1065 			if (-f _) {
       
  1066 				my($File);
       
  1067 
       
  1068 				# Typically shared object dependencies end with
       
  1069 				# ".so" or ".so.?", hence we can reduce the cost
       
  1070 				# of this script (a lot!) by screening out files
       
  1071 				# that don't follow this pattern.
       
  1072 				if (!$opt{a}) {
       
  1073 					if ($Entry !~ /\.so(?:\.\d+)*$/) {
       
  1074 						next;
       
  1075 					}
       
  1076 				}
       
  1077 
       
  1078 				$File = `file $NewFull`;
       
  1079 				if ($File !~ /dynamic lib/) {
       
  1080 					next;
       
  1081 				}
       
  1082 
       
  1083 				if ($File =~ /32-bit/) {
       
  1084 					Entercrle32($FullDir, $RelDir, $Entry);
       
  1085 				} elsif ($Ena64) {
       
  1086 					Entercrle64($FullDir, $RelDir, $Entry);
       
  1087 				}
       
  1088 				next;
       
  1089 			}
       
  1090 		}
       
  1091 		closedir(DIR);
       
  1092 	}
       
  1093 }
       
  1094 exit $Error