Home | History | Annotate | Download | only in scripts
      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.
     39 #
     40 # By default any file that has conditions that should be reported is first
     41 # listed and then each condition follows.  The -o (one-line) option produces a
     42 # more terse output which is better for sorting/diffing with "nightly".
     43 #
     44 # NOTE: missing dependencies, symbols or versions are reported by running the
     45 # file through ldd(1).  As objects within a proto area are built to exist in a
     46 # base system, standard use of ldd(1) will bind any objects to dependencies
     47 # that exist in the base system.  It is frequently the case that newer objects
     48 # exist in the proto area that are required to satisfy other objects
     49 # dependencies, and without using these newer objects an ldd(1) will produce
     50 # misleading error messages.  To compensate for this, the -D/-d options, or the
     51 # existence of the CODEMSG_WS/ROOT environment variables, cause the creation of
     52 # alternative dependency mappings via crle(1) configuration files that establish
     53 # any proto shared objects as alternatives to their base system location.  Thus
     54 # ldd(1) can be executed against these configuration files so that objects in a
     55 # proto area bind to their dependencies in the same proto area.
     56 
     57 
     58 # Define all global variables (required for strict)
     59 use vars  qw($Prog $Env $Ena64 $Tmpdir $Gnuc);
     60 use vars  qw($LddNoU $Conf32 $Conf64);
     61 use vars  qw(%opt);
     62 use vars  qw($ErrFH $ErrTtl $InfoFH $InfoTtl $OutCnt1 $OutCnt2);
     63 
     64 # An exception file is used to specify regular expressions to match
     65 # objects. These directives specify special attributes of the object.
     66 # The regular expressions are read from the file and compiled into the
     67 # regular expression variables.
     68 #
     69 # The name of each regular expression variable is of the form
     70 #
     71 #	$EXRE_xxx
     72 #
     73 # where xxx is the name of the exception in lower case. For example,
     74 # the regular expression variable for EXEC_STACK is $EXRE_exec_stack.
     75 #
     76 # onbld_elfmod::LoadExceptionsToEXRE() depends on this naming convention
     77 # to initialize the regular expression variables, and to detect invalid
     78 # exception names.
     79 #
     80 # If a given exception is not used in the exception file, its regular
     81 # expression variable will be undefined. Users of these variables must
     82 # test the variable with defined() prior to use:
     83 #
     84 #	defined($EXRE_exec_stack) && ($foo =~ $EXRE_exec_stack)
     85 #
     86 # ----
     87 #
     88 # The exceptions are:
     89 #
     90 #   EXEC_STACK
     91 #	Objects that are not required to have a non-executable stack
     92 #
     93 #   NOCRLEALT
     94 #	Objects that should be skipped by AltObjectConfig() when building
     95 #	the crle script that maps objects to the proto area.
     96 #
     97 #    NODIRECT
     98 #	Objects that are not required to use direct bindings
     99 #
    100 #    NOSYMSORT
    101 #	Objects we should not check for duplicate addresses in
    102 #	the symbol sort sections.
    103 #
    104 #    OLDDEP
    105 #	Objects that are no longer needed because their functionalty
    106 #	has migrated elsewhere. These are usually pure filters that
    107 #	point at libc.
    108 #
    109 #    SKIP
    110 #	Files and directories that should be excluded from analysis.
    111 #
    112 #    STAB
    113 #	Objects that are allowed to contain stab debugging sections
    114 #
    115 #    TEXTREL
    116 #	Object for which relocations are allowed to the text segment
    117 #
    118 #    UNDEF_REF
    119 #	Objects that are allowed undefined references
    120 #
    121 #    UNREF_OBJ
    122 #	"unreferenced object=" ldd(1) diagnostics.
    123 #
    124 #    UNUSED_DEPS
    125 #	Objects that are allowed to have unused dependencies
    126 #
    127 #    UNUSED_OBJ
    128 #	Objects that are allowed to be unused dependencies
    129 #
    130 #    UNUSED_RPATH
    131 #	Objects with unused runpaths
    132 #
    133 
    134 use vars  qw($EXRE_exec_stack $EXRE_nocrlealt $EXRE_nodirect $EXRE_nosymsort);
    135 use vars  qw($EXRE_olddep $EXRE_skip $EXRE_stab $EXRE_textrel $EXRE_undef_ref);
    136 use vars  qw($EXRE_unref_obj $EXRE_unused_deps $EXRE_unused_obj);
    137 use vars  qw($EXRE_unused_rpath);
    138 
    139 use strict;
    140 use Getopt::Std;
    141 use File::Basename;
    142 
    143 
    144 # Reliably compare two OS revisions.  Arguments are <ver1> <op> <ver2>.
    145 # <op> is the string form of a normal numeric comparison operator.
    146 sub cmp_os_ver {
    147 	my @ver1 = split(/\./, $_[0]);
    148 	my $op = $_[1];
    149 	my @ver2 = split(/\./, $_[2]);
    150 
    151 	push @ver2, ("0") x $#ver1 - $#ver2;
    152 	push @ver1, ("0") x $#ver2 - $#ver1;
    153 
    154 	my $diff = 0;
    155 	while (@ver1 || @ver2) {
    156 		if (($diff = shift(@ver1) - shift(@ver2)) != 0) {
    157 			last;
    158 		}
    159 	}
    160 	return (eval "$diff $op 0" ? 1 : 0);
    161 }
    162 
    163 ## ProcFile(FullPath, RelPath, File, Class, Type, Verdef)
    164 #
    165 # Determine whether this a ELF dynamic object and if so investigate its runtime
    166 # attributes.
    167 #
    168 sub ProcFile {
    169 	my($FullPath, $RelPath, $Class, $Type, $Verdef) = @_;
    170 	my(@Elf, @Ldd, $Dyn, $Sym, $Stack);
    171 	my($Sun, $Relsz, $Pltsz, $Tex, $Stab, $Strip, $Lddopt, $SymSort);
    172 	my($Val, $Header, $IsX86, $RWX, $UnDep);
    173 	my($HasDirectBinding);
    174 
    175 	# Only look at executables and sharable objects
    176 	return if ($Type ne 'EXEC') && ($Type ne 'DYN');
    177 
    178 	# Ignore symbolic links
    179 	return if -l $FullPath;
    180 
    181 	# Is this an object or directory hierarchy we don't care about?
    182 	return if (defined($EXRE_skip) && ($RelPath =~ $EXRE_skip));
    183 
    184 	# Bail if we can't stat the file. Otherwise, note if it is SUID/SGID.
    185 	return if !stat($FullPath);
    186 	my $Secure = (-u _ || -g _) ? 1 : 0;
    187 
    188 	# Reset output message counts for new input file
    189 	$$ErrTtl = $$InfoTtl = 0;
    190 
    191 	@Ldd = 0;
    192 
    193 	# Determine whether we have access to inspect the file.
    194 	if (!(-r $FullPath)) {
    195 		onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    196 		    "unable to inspect file: permission denied");
    197 		return;
    198 	}
    199 
    200 	# Determine whether we have a executable (static or dynamic) or a
    201 	# shared object.
    202 	@Elf = split(/\n/, `elfdump -epdcy $FullPath 2>&1`);
    203 
    204 	$Dyn = $Stack = $IsX86 = $RWX = 0;
    205 	$Header = 'None';
    206 	foreach my $Line (@Elf) {
    207 		# If we have an invalid file type (which we can tell from the
    208 		# first line), or we're processing an archive, bail.
    209 		if ($Header eq 'None') {
    210 			if (($Line =~ /invalid file/) ||
    211 			    ($Line =~ /$FullPath(.*):/)) {
    212 				return;
    213 			}
    214 		}
    215 
    216 		if ($Line =~ /^ELF Header/) {
    217 			$Header = 'Ehdr';
    218 			next;
    219 		}
    220 
    221 		if ($Line =~ /^Program Header/) {
    222 			$Header = 'Phdr';
    223 			$RWX = 0;
    224 			next;
    225 		}
    226 
    227 		if ($Line =~ /^Dynamic Section/) {
    228 			# A dynamic section indicates we're a dynamic object
    229 			# (this makes sure we don't check static executables).
    230 			$Dyn = 1;
    231 			next;
    232 		}
    233 
    234 		if (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) {
    235 			# If it's a X86 object, we need to enforce RW- data.
    236 			$IsX86 = 1 if $Line =~ /(EM_AMD64|EM_386)/;
    237 			next;
    238 		}
    239 
    240 		if (($Header eq 'Phdr') &&
    241 		    ($Line =~ /\[ PF_X  PF_W  PF_R \]/)) {
    242 			# RWX segment seen.
    243 			$RWX = 1;
    244 			next;
    245 		}
    246 
    247 		if (($Header eq 'Phdr') &&
    248 		    ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) {
    249 			# Seen an RWX PT_LOAD segment.
    250 			if (defined($EXRE_exec_stack) &&
    251 			    ($RelPath !~ $EXRE_exec_stack)) {
    252 				onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    253 				    "application requires non-executable " .
    254 				    "data\t<no -Mmapfile_noexdata?>");
    255 			}
    256 			next;
    257 		}
    258 
    259 		if (($Header eq 'Phdr') && ($Line =~ /\[ PT_SUNWSTACK \]/)) {
    260 			# This object defines a non-executable stack.
    261 			$Stack = 1;
    262 			next;
    263 		}
    264 	}
    265 
    266 	# Determine whether this ELF executable or shared object has a
    267 	# conforming mcs(1) comment section.  If the correct $(POST_PROCESS)
    268 	# macros are used, only a 3 or 4 line .comment section should exist
    269 	# containing one or two "@(#)SunOS" identifying comments (one comment
    270 	# for a non-debug build, and two for a debug build). The results of
    271 	# the following split should be three or four lines, the last empty
    272 	# line being discarded by the split.
    273 	if ($opt{m}) {
    274 		my(@Mcs, $Con, $Dev);
    275 
    276 		@Mcs = split(/\n/, `mcs -p $FullPath 2>&1`);
    277 
    278 		$Con = $Dev = $Val = 0;
    279 		foreach my $Line (@Mcs) {
    280 			$Val++;
    281 
    282 			if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) {
    283 				$Con = 1;
    284 				last;
    285 			}
    286 			if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) {
    287 				$Dev = 1;
    288 				next;
    289 			}
    290 			if (($Dev == 0) && ($Val == 4)) {
    291 				$Con = 1;
    292 				last;
    293 			}
    294 			if (($Dev == 1) && ($Val == 5)) {
    295 				$Con = 1;
    296 				last;
    297 			}
    298 		}
    299 		if ($opt{m} && ($Con == 1)) {
    300 			onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    301 		    "non-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>");
    302 		}
    303 	}
    304 
    305 	# Applications should contain a non-executable stack definition.
    306 	if (($Type eq 'EXEC') && ($Stack == 0) &&
    307 	    (!defined($EXRE_exec_stack) || ($RelPath !~ $EXRE_exec_stack))) {
    308 		onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    309 		    "non-executable stack required\t<no -Mmapfile_noexstk?>");
    310 	}
    311 
    312 	# Having caught any static executables in the mcs(1) check and non-
    313 	# executable stack definition check, continue with dynamic objects
    314 	# from now on.
    315 	if ($Dyn eq 0) {
    316 		return;
    317 	}
    318 
    319 	# Use ldd unless its a 64-bit object and we lack the hardware.
    320 	if (($Class == 32) || $Ena64) {
    321 		my $LDDFullPath = $FullPath;
    322 
    323 		if ($Secure) {
    324 			# The execution of a secure application over an nfs file
    325 			# system mounted nosuid will result in warning messages
    326 			# being sent to /var/adm/messages.  As this type of
    327 			# environment can occur with root builds, move the file
    328 			# being investigated to a safe place first.  In addition
    329 			# remove its secure permission so that it can be
    330 			# influenced by any alternative dependency mappings.
    331 	
    332 			my $File = $RelPath;
    333 			$File =~ s!^.*/!!;      # basename
    334 
    335 			my($TmpPath) = "$Tmpdir/$File";
    336 
    337 			system('cp', $LDDFullPath, $TmpPath);
    338 			chmod 0777, $TmpPath;
    339 			$LDDFullPath = $TmpPath;
    340 		}
    341 
    342 		# Use ldd(1) to determine the objects relocatability and use.
    343 		# By default look for all unreferenced dependencies.  However,
    344 		# some objects have legitimate dependencies that they do not
    345 		# reference.
    346 		if ($LddNoU) {
    347 			$Lddopt = "-ru";
    348 		} else {
    349 			$Lddopt = "-rU";
    350 		}
    351 		@Ldd = split(/\n/, `ldd $Lddopt $Env $LDDFullPath 2>&1`);
    352 		if ($Secure) {
    353 			unlink $LDDFullPath;
    354 		}
    355 	}
    356 
    357 	$Val = 0;
    358 	$Sym = 5;
    359 	$UnDep = 1;
    360 
    361 	foreach my $Line (@Ldd) {
    362 
    363 		if ($Val == 0) {
    364 			$Val = 1;
    365 			# Make sure ldd(1) worked.  One possible failure is that
    366 			# this is an old ldd(1) prior to -e addition (4390308).
    367 			if ($Line =~ /usage:/) {
    368 				$Line =~ s/$/\t<old ldd(1)?>/;
    369 				onbld_elfmod::OutMsg($ErrFH, $ErrTtl,
    370 				    $RelPath, $Line);
    371 				last;
    372 			} elsif ($Line =~ /execution failed/) {
    373 				onbld_elfmod::OutMsg($ErrFH, $ErrTtl,
    374 				    $RelPath, $Line);
    375 				last;
    376 			}
    377 
    378 			# It's possible this binary can't be executed, ie. we've
    379 			# found a sparc binary while running on an intel system,
    380 			# or a sparcv9 binary on a sparcv7/8 system.
    381 			if ($Line =~ /wrong class/) {
    382 				onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    383 				    "has wrong class or data encoding");
    384 				next;
    385 			}
    386 
    387 			# Historically, ldd(1) likes executable objects to have
    388 			# their execute bit set.
    389 			if ($Line =~ /not executable/) {
    390 				onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    391 				    "is not executable");
    392 				next;
    393 			}
    394 		}
    395 
    396 		# Look for "file" or "versions" that aren't found.  Note that
    397 		# these lines will occur before we find any symbol referencing
    398 		# errors.
    399 		if (($Sym == 5) && ($Line =~ /not found\)/)) {
    400 			if ($Line =~ /file not found\)/) {
    401 				$Line =~ s/$/\t<no -zdefs?>/;
    402 			}
    403 			onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
    404 			next;
    405 		}
    406 		# Look for relocations whose symbols can't be found.  Note, we
    407 		# only print out the first 5 relocations for any file as this
    408 		# output can be excessive.
    409 		if ($Sym && ($Line =~ /symbol not found/)) {
    410 			# Determine if this file is allowed undefined
    411 			# references.
    412 			if (($Sym == 5) && defined($EXRE_undef_ref) &&
    413 			    ($RelPath =~ $EXRE_undef_ref)) {
    414 				$Sym = 0;
    415 				next;
    416 			}
    417 			if ($Sym-- == 1) {
    418 				onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    419 				    "continued ...") if !$opt{o};
    420 				next;
    421 			}
    422 			# Just print the symbol name.
    423 			$Line =~ s/$/\t<no -zdefs?>/;
    424 			onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
    425 			next;
    426 		}
    427 		# Look for any unused search paths.
    428 		if ($Line =~ /unused search path=/) {
    429 			# Note, skip this comparison for __GNUC builds, as the
    430 			# gnu compilers insert numerous unused search paths.
    431 			if ($Gnuc == 1) {
    432 				next;
    433 			}
    434 			next if defined($EXRE_unused_rpath) &&
    435 			    ($Line =~ $EXRE_unused_rpath);
    436 
    437 			if ($Secure) {
    438 				$Line =~ s!$Tmpdir/!!;
    439 			}
    440 			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove search path?>/;
    441 			onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
    442 			next;
    443 		}
    444 		# Look for unreferenced dependencies.  Note, if any unreferenced
    445 		# objects are ignored, then set $UnDep so as to suppress any
    446 		# associated unused-object messages.
    447 		if ($Line =~ /unreferenced object=/) {
    448 			if (defined($EXRE_unref_obj) &&
    449 			    ($Line =~ $EXRE_unref_obj)) {
    450 				$UnDep = 0;
    451 				next;
    452 			}
    453 			if ($Secure) {
    454 				$Line =~ s!$Tmpdir/!!;
    455 			}
    456 			$Line =~ s/^[ \t]*(.*)/$1\t<remove lib or -zignore?>/;
    457 			onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
    458 			next;
    459 		}
    460 		# Look for any unused dependencies.
    461 		if ($UnDep && ($Line =~ /unused/)) {
    462 			# Skip if object is allowed to have unused dependencies
    463 			next if defined($EXRE_unused_deps) &&
    464 			    ($RelPath =~ $EXRE_unused_deps);
    465 
    466 			# Skip if dependency is always allowed to be unused
    467 			next if defined($EXRE_unused_obj) &&
    468 			    ($Line =~ $EXRE_unused_obj);
    469 
    470 			$Line =~ s!$Tmpdir/!! if $Secure;
    471 			$Line =~ s/^[ \t]*(.*)/$1\t<remove lib or -zignore?>/;
    472 			onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
    473 			next;
    474 		}
    475 	}
    476 
    477 	# Reuse the elfdump(1) data to investigate additional dynamic linking
    478 	# information.
    479 
    480 	$Sun = $Relsz = $Pltsz = $Dyn = $Stab = $SymSort = 0;
    481 	$Tex = $Strip = 1;
    482 	$HasDirectBinding = 0;
    483 
    484 	$Header = 'None';
    485 ELF:	foreach my $Line (@Elf) {
    486 		# We're only interested in the section headers and the dynamic
    487 		# section.
    488 		if ($Line =~ /^Section Header/) {
    489 			$Header = 'Shdr';
    490 
    491 			if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) {
    492 				# This object has a combined relocation section.
    493 				$Sun = 1;
    494 
    495 			} elsif (($Stab == 0) && ($Line =~ /\.stab/)) {
    496 				# This object contain .stabs sections
    497 				$Stab = 1;
    498 			} elsif (($SymSort == 0) &&
    499 				 ($Line =~ /\.SUNW_dyn(sym)|(tls)sort/)) {
    500 				# This object contains a symbol sort section
    501 				$SymSort = 1;
    502 			}
    503 
    504 			if (($Strip == 1) && ($Line =~ /\.symtab/)) {
    505 				# This object contains a complete symbol table.
    506 				$Strip = 0;
    507 			}
    508 			next;
    509 
    510 		} elsif ($Line =~ /^Dynamic Section/) {
    511 			$Header = 'Dyn';
    512 			next;
    513 		} elsif ($Line =~ /^Syminfo Section/) {
    514 			$Header = 'Syminfo';
    515 			next;
    516 		} elsif (($Header ne 'Dyn') && ($Header ne 'Syminfo')) {
    517 			next;
    518 		}
    519 
    520 		# Look into the Syminfo section.
    521 		# Does this object have at least one Directly Bound symbol?
    522 		if (($Header eq 'Syminfo')) {
    523 			my(@Symword);
    524 
    525 			if ($HasDirectBinding == 1) {
    526 				next;
    527 			}
    528 
    529 			@Symword = split(' ', $Line);
    530 
    531 			if (!defined($Symword[1])) {
    532 				next;
    533 			}
    534 			if ($Symword[1] =~ /B/) {
    535 				$HasDirectBinding = 1;
    536 			}
    537 			next;
    538 		}
    539 
    540 		# Does this object contain text relocations.
    541 		if ($Tex && ($Line =~ /TEXTREL/)) {
    542 			# Determine if this file is allowed text relocations.
    543 			if (defined($EXRE_textrel) &&
    544 			    ($RelPath =~ $EXRE_textrel)) {
    545 				$Tex = 0;
    546 				next ELF;
    547 			}
    548 			onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    549 			    "TEXTREL .dynamic tag\t\t\t<no -Kpic?>");
    550 			$Tex = 0;
    551 			next;
    552 		}
    553 
    554 		# Does this file have any relocation sections (there are a few
    555 		# psr libraries with no relocations at all, thus a .SUNW_reloc
    556 		# section won't exist either).
    557 		if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) {
    558 			$Relsz = hex((split(' ', $Line))[2]);
    559 			next;
    560 		}
    561 
    562 		# Does this file have any plt relocations.  If the plt size is
    563 		# equivalent to the total relocation size then we don't have
    564 		# any relocations suitable for combining into a .SUNW_reloc
    565 		# section.
    566 		if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) {
    567 			$Pltsz = hex((split(' ', $Line))[2]);
    568 			next;
    569 		}
    570 
    571 		# Does this object have any dependencies.
    572 		if ($Line =~ /NEEDED/) {
    573 			my($Need) = (split(' ', $Line))[3];
    574 
    575 			if (defined($EXRE_olddep) && ($Need =~ $EXRE_olddep)) {
    576 				# Catch any old (unnecessary) dependencies.
    577 				onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    578 			"NEEDED=$Need\t<dependency no longer necessary>");
    579 			} elsif ($opt{i}) {
    580 				# Under the -i (information) option print out
    581 				# any useful dynamic entries.
    582 				onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath,
    583 				    "NEEDED=$Need");
    584 			}
    585 			next;
    586 		}
    587 
    588 		# Is this object built with -B direct flag on?
    589 		if ($Line =~ / DIRECT /) {
    590 			$HasDirectBinding = 1;
    591 		}
    592 
    593 		# Does this object specify a runpath.
    594 		if ($opt{i} && ($Line =~ /RPATH/)) {
    595 			my($Rpath) = (split(' ', $Line))[3];
    596 			onbld_elfmod::OutMsg($InfoFH, $InfoTtl,
    597 			    $RelPath, "RPATH=$Rpath");
    598 			next;
    599 		}
    600 	}
    601 
    602 	# A shared object, that contains non-plt relocations, should have a
    603 	# combined relocation section indicating it was built with -z combreloc.
    604 	if (($Type eq 'DYN') && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) {
    605 		onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    606 		    "SUNW_reloc section missing\t\t<no -zcombreloc?>");
    607 	}
    608 
    609 	# No objects released to a customer should have any .stabs sections
    610 	# remaining, they should be stripped.
    611 	if ($opt{s} && $Stab) {
    612 		goto DONESTAB if defined($EXRE_stab) && ($RelPath =~ $EXRE_stab);
    613 
    614 		onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    615 		    "debugging sections should be deleted\t<no strip -x?>");
    616 	}
    617 
    618 	# Identify an object that is not built with either -B direct or
    619 	# -z direct.
    620 	goto DONESTAB
    621 	    if (defined($EXRE_nodirect) && ($RelPath =~ $EXRE_nodirect));
    622 
    623 	if ($Relsz && ($HasDirectBinding == 0)) {
    624 		onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    625 		 "object has no direct bindings\t<no -B direct or -z direct?>");
    626 	}
    627 
    628 DONESTAB:
    629 
    630 	# All objects should have a full symbol table to provide complete
    631 	# debugging stack traces.
    632 	onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    633 	    "symbol table should not be stripped\t<remove -s?>") if $Strip;
    634 
    635 	# If there are symbol sort sections in this object, report on
    636 	# any that have duplicate addresses.
    637 	ProcSymSort($FullPath, $RelPath) if $SymSort;
    638 
    639 	# If -v was specified, and the object has a version definition
    640 	# section, generate output showing each public symbol and the
    641 	# version it belongs to.
    642 	ProcVerdef($FullPath, $RelPath)
    643 	    if ($Verdef eq 'VERDEF') && $opt{v};
    644 }
    645 
    646 
    647 ## ProcSymSortOutMsg(RelPath, secname, addr, names...)
    648 #
    649 # Call onbld_elfmod::OutMsg for a duplicate address error in a symbol sort
    650 # section
    651 #
    652 sub ProcSymSortOutMsg {
    653 	my($RelPath, $secname, $addr, @names) = @_;
    654 
    655 	onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    656 	    "$secname: duplicate $addr: ". join(', ', @names));
    657 }
    658 
    659 
    660 ## ProcSymSort(FullPath, RelPath)
    661 #
    662 # Examine the symbol sort sections for the given object and report
    663 # on any duplicate addresses found.  Ideally, mapfile directives
    664 # should be used when building objects that have multiple symbols
    665 # with the same address so that only one of them appears in the sort
    666 # section. This saves space, reduces user confusion, and ensures that
    667 # libproc and debuggers always display public names instead of symbols
    668 # that are merely implementation details.
    669 #
    670 sub ProcSymSort {
    671 
    672 	my($FullPath, $RelPath) = @_;
    673 
    674 	# If this object is exempt from checking, return quietly
    675 	return if defined($EXRE_nosymsort) && ($FullPath =~ $EXRE_nosymsort);
    676 
    677 
    678 	open(SORT, "elfdump -S $FullPath|") ||
    679 	    die "$Prog: Unable to execute elfdump (symbol sort sections)\n";
    680 
    681 	my $line;
    682 	my $last_addr;
    683 	my @dups = ();
    684 	my $secname;
    685 	while ($line = <SORT>) {
    686 		chomp $line;
    687 		
    688 		next if ($line eq '');
    689 
    690 		# If this is a header line, pick up the section name
    691 		if ($line =~ /^Symbol Sort Section:\s+([^\s]+)\s+/) {
    692 			$secname = $1;
    693 
    694 			# Every new section is followed by a column header line
    695 			$line = <SORT>;		# Toss header line
    696 
    697 			# Flush anything left from previous section
    698 			ProcSymSortOutMsg($RelPath, $secname, $last_addr, @dups)
    699 			    if (scalar(@dups) > 1);
    700 
    701 			# Reset variables for new sort section
    702 			$last_addr = '';
    703 			@dups = ();
    704 
    705 			next;
    706 		}
    707 
    708 		# Process symbol line
    709 		my @fields = split /\s+/, $line;
    710 		my $new_addr = $fields[2]; 
    711 		my $new_type = $fields[8];
    712 		my $new_name = $fields[9]; 
    713 
    714 		if ($new_type eq 'UNDEF') {
    715 			onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
    716 			    "$secname: unexpected UNDEF symbol " .
    717 			    "(link-editor error): $new_name");
    718 			next;
    719 		}
    720 
    721 		if ($new_addr eq $last_addr) {
    722 			push @dups, $new_name;
    723 		} else {
    724 			ProcSymSortOutMsg($RelPath, $secname,
    725 			    $last_addr, @dups) if (scalar(@dups) > 1);
    726 			@dups = ( $new_name );
    727 			$last_addr = $new_addr; 
    728 		}
    729 	}
    730 
    731 	ProcSymSortOutMsg($RelPath, $secname, $last_addr, @dups)
    732 		if (scalar(@dups) > 1);
    733 	
    734 	close SORT;
    735 }
    736 
    737 
    738 ## ProcVerdef(FullPath, RelPath)
    739 #
    740 # Examine the version definition section for the given object and report
    741 # each public symbol along with the version it belongs to.
    742 #
    743 sub ProcVerdef {
    744 
    745 	my($FullPath, $RelPath) = @_;
    746 	my $line;
    747 	my $cur_ver = '';
    748 	my $tab = $opt{o} ? '' : "\t";
    749 
    750 	# pvs -dov provides information about the versioning hierarchy
    751 	# in the file. Lines are of the format:
    752 	#	path - version[XXX];
    753 	# where [XXX] indicates optional information, such as flags
    754 	# or inherited versions.
    755 	#
    756 	# Private versions are allowed to change freely, so ignore them.
    757 	open(PVS, "pvs -dov $FullPath|") ||
    758 	    die "$Prog: Unable to execute pvs (version definition section)\n";
    759 
    760 	while ($line = <PVS>) {
    761 		chomp $line;
    762 
    763 		if ($line =~ /^[^\s]+\s+-\s+([^;]+)/) {
    764 			my $ver = $1;
    765 
    766 			next if $ver =~ /private/i;
    767 			onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath,
    768 			    "${tab}VERDEF=$ver");
    769 		}
    770 	}
    771 	close PVS;
    772 
    773 	# pvs -dos lists the symbols assigned to each version definition.
    774 	# Lines are of the format:
    775 	#	path - version: symbol;
    776 	#	path - version: symbol (size);
    777 	# where the (size) is added to data items, but not for functions.
    778 	# We strip off the size, if present.
    779 
    780 	open(PVS, "pvs -dos $FullPath|") ||
    781 	    die "$Prog: Unable to execute pvs (version definition section)\n";
    782 	while ($line = <PVS>) {
    783 		chomp $line;
    784 		if ($line =~ /^[^\s]+\s+-\s+([^:]+):\s*([^\s;]+)/) {
    785 		    my $ver = $1;
    786 		    my $sym = $2;
    787 
    788 		    next if $ver =~ /private/i;
    789 
    790 		    if ($opt{o}) {
    791 			onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath,
    792 			    "VERSION=$ver, SYMBOL=$sym");
    793 		    } else {
    794 			if ($cur_ver ne $ver) {
    795 			    onbld_elfmod::OutMsg($InfoFH, $InfoTtl,
    796 			        $RelPath, "VERSION=$ver");
    797 			    $cur_ver = $ver;
    798 			}			    
    799 			onbld_elfmod::OutMsg($InfoFH, $InfoTtl,
    800 			    $RelPath, "SYMBOL=$sym");
    801 		    }
    802 		}
    803 	}
    804 	
    805 	close PVS;
    806 }
    807 
    808 
    809 ## OpenFindElf(file, FileHandleRef, LineNumRef)
    810 #
    811 # Open file in 'find_elf -r' format, and return the value of
    812 # the opening PREFIX line.
    813 #
    814 # entry:
    815 #	file - file, or find_elf child process, to open
    816 #	FileHandleRef - Reference to file handle to open
    817 #	LineNumRef - Reference to integer to increment as lines are input
    818 #
    819 # exit:
    820 #	This routine issues a fatal error and does not return on error.
    821 #	Otherwise, the value of PREFIX is returned.
    822 #
    823 sub OpenFindElf {
    824 	my ($file, $fh, $LineNum) = @_;
    825 	my $line;
    826 	my $prefix;
    827 
    828 	open($fh, $file) || die "$Prog: Unable to open: $file";
    829 	$$LineNum = 0;
    830 
    831 	# This script requires relative paths as created by 'find_elf -r'.
    832 	# When this is done, the first non-comment line will always
    833 	# be PREFIX. Obtain that line, or issue a fatal error.
    834 	while ($line = onbld_elfmod::GetLine($fh, $LineNum)) {
    835 		if ($line =~ /^PREFIX\s+(.*)$/i) {
    836 			$prefix = $1;
    837 			last;
    838 		}
    839 
    840 		die "$Prog: No PREFIX line seen on line $$LineNum: $file";
    841 	}
    842 
    843 	$prefix;
    844 }
    845 
    846 
    847 ## ProcFindElf(file)
    848 #
    849 # Open the specified file, which must be produced by "find_elf -r",
    850 # and process the files it describes.
    851 #
    852 sub ProcFindElf {
    853 	my $file = $_[0];
    854 	my $line;
    855 	my $LineNum;
    856 
    857 	my $prefix = OpenFindElf($file, \*FIND_ELF, \$LineNum);
    858 
    859 	while ($line = onbld_elfmod::GetLine(\*FIND_ELF, \$LineNum)) {
    860 		next if !($line =~ /^OBJECT\s/i);
    861 
    862 		my ($item, $class, $type, $verdef, $obj) =
    863 		    split(/\s+/, $line, 5);
    864 
    865 		ProcFile("$prefix/$obj", $obj, $class, $type, $verdef);
    866 	}
    867 
    868 	close FIND_ELF;
    869 }
    870 
    871 
    872 ## AltObjectConfig(file)
    873 #
    874 # Recurse through a directory hierarchy looking for appropriate dependencies
    875 # to map from their standard system locations to the proto area via a crle
    876 # config file.
    877 #
    878 # entry:
    879 #	file - File of ELF objects, in 'find_elf -r' format, to examine.
    880 #
    881 # exit:
    882 #	Scripts are generated for the 32 and 64-bit cases to run crle
    883 #	and create runtime configuration files that will establish
    884 #	alternative dependency mappings for the objects identified.
    885 #
    886 #	$Env - Set to environment variable definitions that will cause
    887 #		the config files generated by this routine to be used
    888 #		by ldd.
    889 #	$Conf32, $Conf64 - Undefined, or set to the config files generated
    890 #		by this routine. If defined, the caller is responsible for
    891 #		unlinking the files before exiting.
    892 #
    893 sub AltObjectConfig {
    894 	my $file = $_[0];
    895 	my ($Crle32, $Crle64);
    896 	my $line;
    897 	my $LineNum;
    898 	my $obj_path;
    899 	my $obj_active = 0;
    900 	my $obj_class;
    901 
    902 	my $prefix = OpenFindElf($file, \*FIND_ELF);
    903 
    904 LINE:
    905 	while ($line = onbld_elfmod::GetLine(\*FIND_ELF, \$LineNum)) {
    906 	      ITEM: {
    907 
    908 			if ($line =~ /^OBJECT\s/i) {
    909 				my ($item, $class, $type, $verdef, $obj) =
    910 				    split(/\s+/, $line, 5);
    911 
    912 				if ($type eq 'DYN') {
    913 					$obj_active = 1;
    914 					$obj_path = $obj;
    915 					$obj_class = $class;
    916 				} else {
    917 					# Only want sharable objects
    918 					$obj_active = 0;
    919 				}
    920 				last ITEM;
    921 			}
    922 
    923 			# We need to follow links to sharable objects so
    924 			# that any dependencies are expressed in all their
    925 			# available forms. We depend on ALIAS lines directly
    926 			# following the object they alias, so if we have
    927 			# a current object, this alias belongs to it.
    928 			if ($obj_active && ($line =~ /^ALIAS\s/i)) {
    929 				my ($item, $real_obj, $obj) =
    930 				    split(/\s+/, $line, 3);
    931 				$obj_path = $obj;
    932 				last ITEM;
    933 			}
    934 
    935 			# Skip unrecognized item
    936 			next LINE;
    937 		}
    938 
    939 		next if !$obj_active;
    940 
    941 		my $full = "$prefix/$obj_path";
    942 
    943 		next if defined($EXRE_nocrlealt) &&
    944 		    ($obj_path =~ $EXRE_nocrlealt);
    945 
    946 		my $Dir = $full;
    947 		$Dir =~ s/^(.*)\/.*$/$1/;
    948 
    949 		# Create a crle(1) script for the dependency we've found.
    950 		# We build separate scripts for the 32 and 64-bit cases.
    951 		# We create and initialize each script when we encounter
    952 		# the first object that needs it.
    953 		if ($obj_class == 32) {
    954 			if (!$Crle32) {
    955 				$Crle32 = "$Tmpdir/$Prog.crle32.$$";
    956 				open(CRLE32, "> $Crle32") ||
    957 				    die "$Prog: open failed: $Crle32: $!";
    958 				print CRLE32 "#!/bin/sh\ncrle \\\n";
    959 			}
    960 			print CRLE32 "\t-o $Dir -a /$obj_path \\\n";
    961 		} elsif ($Ena64) {
    962 			if (!$Crle64) {
    963 				$Crle64 = "$Tmpdir/$Prog.crle64.$$";
    964 				open(CRLE64, "> $Crle64") ||
    965 				    die "$Prog: open failed: $Crle64: $!";
    966 				print CRLE64 "#!/bin/sh\ncrle -64\\\n";
    967 			}
    968 			print CRLE64 "\t-o $Dir -a /$obj_path \\\n";
    969 		}
    970 	}
    971 
    972 	close FIND_ELF;
    973 
    974 
    975 	# Now that the config scripts are complete, use them to generate
    976 	# runtime linker config files.
    977 	if ($Crle64) {
    978 		$Conf64 = "$Tmpdir/$Prog.conf64.$$";
    979 		print CRLE64 "\t-c $Conf64\n";
    980 
    981 		chmod 0755, $Crle64;
    982 		close CRLE64;
    983 
    984 		undef $Conf64 if system($Crle64);
    985 
    986 		# Done with the script
    987 		unlink $Crle64;
    988 	}
    989 	if ($Crle32) {
    990 		$Conf32 = "$Tmpdir/$Prog.conf32.$$";
    991 		print CRLE32 "\t-c $Conf32\n";
    992 
    993 		chmod 0755, $Crle32;
    994 		close CRLE32;
    995 
    996 		undef $Conf32 if system($Crle32);
    997 
    998 		# Done with the script
    999 		unlink $Crle32;
   1000 	}
   1001 
   1002 	# Set $Env so that we will use the config files generated above
   1003 	# when we run ldd.
   1004 	if ($Crle64 && $Conf64 && $Crle32 && $Conf32) {
   1005 		$Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32";
   1006 	} elsif ($Crle64 && $Conf64) {
   1007 		$Env = "-e LD_FLAGS=config_64=$Conf64";
   1008 	} elsif ($Crle32 && $Conf32) {
   1009 		$Env = "-e LD_FLAGS=config_32=$Conf32";
   1010 	}
   1011 }
   1012 
   1013 # -----------------------------------------------------------------------------
   1014 
   1015 # This script relies on ldd returning output reflecting only the binary 
   1016 # contents.  But if LD_PRELOAD* environment variables are present, libraries
   1017 # named by them will also appear in the output, disrupting our analysis.
   1018 # So, before we get too far, scrub the environment.
   1019 
   1020 delete($ENV{LD_PRELOAD});
   1021 delete($ENV{LD_PRELOAD_32});
   1022 delete($ENV{LD_PRELOAD_64});
   1023 
   1024 # Establish a program name for any error diagnostics.
   1025 chomp($Prog = `basename $0`);
   1026 
   1027 # The onbld_elfmod package is maintained in the same directory as this
   1028 # script, and is installed in ../lib/perl. Use the local one if present,
   1029 # and the installed one otherwise.
   1030 my $moddir = dirname($0);
   1031 $moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm";
   1032 require "$moddir/onbld_elfmod.pm";
   1033 
   1034 # Determine what machinery is available.
   1035 my $Mach = `uname -p`;
   1036 my$Isalist = `isalist`;
   1037 if ($Mach =~ /sparc/) {
   1038 	if ($Isalist =~ /sparcv9/) {
   1039 		$Ena64 = "ok";
   1040 	}
   1041 } elsif ($Mach =~ /i386/) {
   1042 	if ($Isalist =~ /amd64/) {
   1043 		$Ena64 = "ok";
   1044 	}
   1045 }
   1046 
   1047 # $Env is used with all calls to ldd. It is set by AltObjectConfig to
   1048 # cause an alternate object mapping runtime config file to be used.
   1049 $Env = '';
   1050 
   1051 # Check that we have arguments.
   1052 if ((getopts('D:d:E:e:f:I:imosvw:', \%opt) == 0) ||
   1053     (!$opt{f} && ($#ARGV == -1))) {
   1054 	print "usage: $Prog [-imosv] [-D depfile | -d depdir] [-E errfile]\n";
   1055 	print "\t\t[-e exfile] [-f listfile] [-I infofile] [-w outdir]\n";
   1056 	print "\t\t[file | dir]...\n";
   1057 	print "\n";
   1058 	print "\t[-D depfile]\testablish dependencies from 'find_elf -r' file list\n";
   1059 	print "\t[-d depdir]\testablish dependencies from under directory\n";
   1060 	print "\t[-E errfile]\tdirect error output to file\n";
   1061 	print "\t[-e exfile]\texceptions file\n";
   1062 	print "\t[-f listfile]\tuse file list produced by find_elf -r\n";
   1063 	print "\t[-I infofile]\tdirect informational output (-i, -v) to file\n";
   1064 	print "\t[-i]\t\tproduce dynamic table entry information\n";
   1065 	print "\t[-m]\t\tprocess mcs(1) comments\n";
   1066 	print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
   1067 	print "\t[-s]\t\tprocess .stab and .symtab entries\n";
   1068 	print "\t[-v]\t\tprocess version definition entries\n";
   1069 	print "\t[-w outdir]\tinterpret all files relative to given directory\n";
   1070 	exit 1;
   1071 }
   1072 
   1073 die "$Prog: -D and -d options are mutually exclusive\n" if ($opt{D} && $opt{d});
   1074 
   1075 $Tmpdir = "/tmp" if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir));
   1076 
   1077 # Determine whether this is a __GNUC build.  If so, unused search path
   1078 # processing is disabled.
   1079 $Gnuc = defined $ENV{__GNUC} ? 1 : 0;
   1080 
   1081 # If -w, change working directory to given location
   1082 !$opt{w} || chdir($opt{w}) || die "$Prog: can't cd to $opt{w}";
   1083 
   1084 # Locate and process the exceptions file
   1085 onbld_elfmod::LoadExceptionsToEXRE('check_rtime');
   1086 
   1087 # Is there a proto area available, either via the -d option, or because
   1088 # we are part of an activated workspace?
   1089 my $Proto;
   1090 if ($opt{d}) {
   1091 	# User specified dependency directory - make sure it exists.
   1092 	-d $opt{d} || die "$Prog: $opt{d} is not a directory\n";
   1093 	$Proto = $opt{d};
   1094 } elsif ($ENV{CODEMGR_WS}) {
   1095 	my $Root;
   1096 
   1097 	# Without a user specified dependency directory see if we're
   1098 	# part of a codemanager workspace and if a proto area exists.
   1099 	$Proto = $Root if ($Root = $ENV{ROOT}) && (-d $Root);
   1100 }
   1101 
   1102 # If we are basing this analysis off the sharable objects found in
   1103 # a proto area, then gather dependencies and construct an alternative
   1104 # dependency mapping via a crle(1) configuration file.
   1105 #
   1106 # To support alternative dependency mapping we'll need ldd(1)'s
   1107 # -e option.  This is relatively new (s81_30), so make sure
   1108 # ldd(1) is capable before gathering any dependency information.
   1109 if ($opt{D} || $Proto) {
   1110 	if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) {
   1111 		print "ldd: does not support -e, unable to ";
   1112 		print "create alternative dependency mappingings.\n";
   1113 		print "ldd: option added under 4390308 (s81_30).\n\n";
   1114 	} else {
   1115 		# If -D was specified, it supplies a list of files in
   1116 		# 'find_elf -r' format, and can use it directly. Otherwise,
   1117 		# we will run find_elf as a child process to find the
   1118 		# sharable objects found under $Proto.
   1119 		AltObjectConfig($opt{D} ? $opt{D} : "find_elf -frs $Proto|");
   1120 	}
   1121 }
   1122 
   1123 # To support unreferenced dependency detection we'll need ldd(1)'s -U
   1124 # option.  This is relatively new (4638070), and if not available we
   1125 # can still fall back to -u.  Even with this option, don't use -U with
   1126 # releases prior to 5.10 as the cleanup for -U use only got integrated
   1127 # into 5.10 under 4642023.  Note, that nightly doesn't typically set a
   1128 # RELEASE from the standard <env> files.  Users who wish to disable use
   1129 # of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file
   1130 # if using nightly, or otherwise establish it in their environment.
   1131 if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) {
   1132 	$LddNoU = 1;
   1133 } else {
   1134 	my($Release);
   1135 
   1136 	if (($Release = $ENV{RELEASE}) && (cmp_os_ver($Release, "<", "5.10"))) {
   1137 		$LddNoU = 1;
   1138 	} else {
   1139 		$LddNoU = 0;
   1140 	}
   1141 }
   1142 
   1143 # Set up variables used to handle output files:
   1144 #
   1145 # Error messages go to stdout unless -E is specified. $ErrFH is a
   1146 # file handle reference that points at the file handle where error messages
   1147 # are sent, and $ErrTtl is a reference that points at an integer used
   1148 # to count how many lines have been sent there.
   1149 #
   1150 # Informational messages go to stdout unless -I is specified. $InfoFH is a
   1151 # file handle reference that points at the file handle where info messages
   1152 # are sent, and $InfoTtl is a reference that points at an integer used
   1153 # to count how many lines have been sent there.
   1154 #
   1155 if ($opt{E}) {
   1156 	open(ERROR, ">$opt{E}") || die "$Prog: open failed: $opt{E}";
   1157 	$ErrFH = \*ERROR;
   1158 } else {
   1159 	$ErrFH = \*STDOUT;
   1160 }
   1161 
   1162 if ($opt{I}) {
   1163 	open(INFO, ">$opt{I}") || die "$Prog: open failed: $opt{I}";
   1164 	$InfoFH = \*INFO;
   1165 } else {
   1166 	$InfoFH = \*STDOUT;
   1167 }
   1168 my ($err_dev, $err_ino) = stat($ErrFH);
   1169 my ($info_dev, $info_ino) = stat($InfoFH);
   1170 $ErrTtl = \$OutCnt1;
   1171 $InfoTtl = (($err_dev == $info_dev) && ($err_ino == $info_ino)) ?
   1172     \$OutCnt1 : \$OutCnt2;
   1173 
   1174 
   1175 # If we were given a list of objects in 'find_elf -r' format, then
   1176 # process it.
   1177 ProcFindElf($opt{f}) if $opt{f};
   1178 
   1179 # Process each argument
   1180 foreach my $Arg (@ARGV) {
   1181 	# Run find_elf to find the files given by $Arg and process them
   1182 	ProcFindElf("find_elf -fr $Arg|");
   1183 }
   1184 
   1185 # Cleanup output files
   1186 unlink $Conf64 if $Conf64;
   1187 unlink $Conf32 if $Conf32;
   1188 close ERROR if $opt{E};
   1189 close INFO if $opt{I};
   1190 
   1191 exit 0;
   1192