Home | History | Annotate | Download | only in lari
      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 2008 Sun Microsystems, Inc.  All rights reserved.
     25 # Use is subject to license terms.
     26 #
     27 # Link Analysis of Runtime Interfaces.
     28 #
     29 
     30 # Define all global variables (required for strict)
     31 use vars  qw($Prog $DestDir $ObjRef $ObjFlag $ObjSize $ObjVis $TmpDir);
     32 use vars  qw($LddArgs $SymFlag);
     33 use vars  qw($Glob $Intp $Dirc $Cpyr $Prot $Extn $Self $Gfte $Plta $User $Func);
     34 use vars  qw($Rejt $Sfte $Afte $Objt $Nodi $Osft $Oaft $Ssft $Saft $Msft);
     35 use vars  qw($Rtld $GlobWeak $MultSyms $CrtSyms $Platform $DbgSeed %opt);
     36 
     37 # Global arrays that must be cleared for multi input file use.
     38 use vars  qw(%Symbols %Objects %Versioned %DemSyms %ObjFltrs %SymFltes);
     39 
     40 use strict;
     41 
     42 use Getopt::Std;
     43 use File::Basename;
     44 
     45 # Pattern match to skip the runtime linker.
     46 $Rtld = qr{
     47 	/lib/ld\.so\.1 |
     48 	/usr/lib/ld\.so\.1 |
     49 	/lib/sparcv9/ld\.so\.1 |
     50 	/usr/lib/sparcv9/ld\.so\.1 |
     51 	/lib/amd64/ld\.so\.1 |
     52 	/usr/lib/amd64/ld\.so\.1
     53 }x;
     54 
     55 # Pattern matching required to determine a global symbol.
     56 $GlobWeak = qr{ ^(?:
     57 	GLOB |
     58 	WEAK
     59 	)$
     60 }x;
     61 
     62 # Pattern matching to determine link-editor specific symbols and those common
     63 # to the compilation environment (ie. provided by all crt's).
     64 $MultSyms = qr{ ^(?:
     65 	 _DYNAMIC |
     66 	 _GLOBAL_OFFSET_TABLE_ |
     67 	 _PROCEDURE_LINKAGE_TABLE_ |
     68 	 _etext |
     69 	 _edata |
     70 	 _end |
     71 	 _init |
     72 	 _fini |
     73 	 _lib_version |			# Defined in values
     74 	 __xpg4 |			# Defined in values
     75 	 __xpg6				# Defined in values
     76 	)$
     77 }x;
     78 
     79 $CrtSyms = qr{ ^(?:
     80 	 ___Argv |			# Defined in crt
     81 	 __environ_lock |		# Defined in crt
     82 	 _environ |			# Defined in crt
     83 	 environ			# Defined in crt
     84 	 )$
     85 }x;
     86 
     87 # Symbol flags.
     88 $Glob = 0x00001;	# symbol is global
     89 $Sfte = 0x00002;	# symbol is a filtee backing a standard filter
     90 $Afte = 0x00004;	# symbol is a filtee backing a auxiliary filter
     91 $Gfte = 0x00008;	# symbol bound as a filtee
     92 $Intp = 0x00010;	# symbol originates from explicit interposer
     93 $Dirc = 0x00020;	# symbol bound to directly
     94 $Cpyr = 0x00040;	# symbol bound to copy-relocation reference
     95 $Prot = 0x00080;	# symbol is protected (symbolic)
     96 $Extn = 0x00100;	# symbol has been bound to from an external reference
     97 $Self = 0x00200;	# symbol has been bound to from the same object
     98 $Rejt = 0x00400;	# symbol binding was (at some point) rejected
     99 $Plta = 0x00800;	# symbol bound to executables plt address
    100 $User = 0x01000;	# symbol binding originates from user (dlsym) request
    101 $Func = 0x02000;	# symbol is of type function
    102 $Objt = 0x04000;	# symbol is of type object
    103 $Nodi = 0x08000;	# symbol prohibits direct binding
    104 
    105 $Osft = 0x10000;	# symbol is an standard object filter
    106 $Oaft = 0x20000;	# symbol is an auxiliary object filter
    107 $Ssft = 0x40000;	# symbol is a per-symbol standard filter
    108 $Saft = 0x80000;	# symbol is a per-symbol auxiliary filter
    109 $Msft = 0xf0000;	# filter mask
    110 
    111 # Offsets into $Symbols{$SymName}{$Obj} array.
    112 $ObjRef =	0;
    113 $ObjFlag =	1;
    114 $ObjSize =	2;
    115 $ObjVis =	3;
    116 
    117 # Offset into $SymFltr{$SymName}{$Filtee} array.
    118 $SymFlag = 	0;
    119 
    120 # Establish locale
    121 use POSIX qw(locale_h);
    122 use Sun::Solaris::Utils qw(textdomain gettext);
    123 
    124 setlocale(LC_ALL, "");
    125 textdomain("SUNW_OST_SGS");
    126 
    127 # Establish a program name for any error diagnostics.
    128 $Prog = basename($0);
    129 
    130 sub inappropriate {
    131 	my ($Opt1, $Opt2, $Flag) = @_;
    132 
    133 	if ($Flag) {
    134 	    printf STDERR
    135 		gettext("%s: inappropriate use of %s with %s: %s ignored\n"),
    136 		$Prog, $Opt1, $Opt2, $Opt1;
    137 	} else {
    138 	    printf STDERR
    139 		gettext("%s: inappropriate use of %s without %s: %s ignored\n"),
    140 		$Prog, $Opt1, $Opt2, $Opt1;
    141 	}
    142 }
    143 
    144 # Cleanup any temporary files on interruption.
    145 sub Cleanup {
    146 	my ($Sig) = @_;
    147 
    148 	$SIG{$Sig} = 'IGNORE';
    149 
    150 	if ($DbgSeed ne "") {
    151 		foreach my $File (<\Q${DbgSeed}\E.*>) {
    152 			if ($File =~ /^\Q$DbgSeed\E\.\d+$/) {
    153 				unlink($File);
    154 			}
    155 		}
    156 	}
    157 	exit 1;
    158 }
    159 
    160 # Check that we have arguments.
    161 if ((getopts('abCDd:imosVv', \%opt) == 0) || ($#ARGV < 0)) {
    162 	printf STDERR gettext("usage:\n");
    163 	printf STDERR
    164 	    gettext("    %s [-bCDsVv] [-a | -i | -o ] file | dir ...\n"), $Prog;
    165 	printf STDERR
    166 	    gettext("    %s [-CDosVv] [-m [-d mapdir]] file\n"), $Prog;
    167 	print STDERR
    168 	    gettext("\t[-a]     print diagnostics for all symbols\n");
    169 	print STDERR
    170 	    gettext("\t[-b]     limit diagnostics to bound symbols\n");
    171 	print STDERR
    172 	    gettext("\t[-C]     print demangled symbol names also\n");
    173 	print STDERR
    174 	    gettext("\t[-D]     read debugging information from \"file\"\n");
    175 	print STDERR
    176 	    gettext("\t[-d dir] create mapfiles in \"mapdir\"\n");
    177 	print STDERR
    178 	    gettext("\t[-i]     print interesting information (default)\n");
    179 	print STDERR
    180 	    gettext("\t[-m]     create mapfiles for interface requirements\n");
    181 	print STDERR
    182 	    gettext("\t[-o]     limit diagnostics to overhead information\n");
    183 	print STDERR
    184 	    gettext("\t[-s]     save bindings information created by ldd(1)\n");
    185 	print STDERR
    186 	    gettext("\t[-V]     append interesting symbol visibilities\n");
    187 	print STDERR
    188 	    gettext("\t[-v]     ignore versioned objects\n");
    189 	exit 1;
    190 } else {
    191 	my ($Mult, $Error);
    192 
    193 	# Catch any incompatible argument usage.
    194 	if ($opt{m}) {
    195 		if ($opt{a}) {
    196 			inappropriate("-a", "-m", 1);
    197 			$opt{a} = 0;
    198 		}
    199 		if ($opt{i}) {
    200 			inappropriate("-i", "-m", 1);
    201 			$opt{i} = 0;
    202 		}
    203 	} else {
    204 		if ($opt{d}) {
    205 			inappropriate("-d", "-m", 0);
    206 			$opt{d} = 0;
    207 		}
    208 	}
    209 	if ($opt{a}) {
    210 		if ($opt{o}) {
    211 			inappropriate("-a", "-o", 1);
    212 			$opt{o} = 0;
    213 		}
    214 		if ($opt{i}) {
    215 			inappropriate("-a", "-i", 1);
    216 			$opt{i} = 0;
    217 		}
    218 	}
    219 	if ($opt{o}) {
    220 		if ($opt{i}) {
    221 			inappropriate("-o", "-i", 1);
    222 			$opt{i} = 0;
    223 		}
    224 		if ($opt{b}) {
    225 			inappropriate("-o", "-b", 1);
    226 			$opt{b} = 0;
    227 		}
    228 	}
    229 
    230 	# If -m is used, only one input file is applicable.
    231 	if ($opt{m} && ($#ARGV != 0)) {
    232 		printf STDERR gettext("%s: only one input file is allowed " .
    233 		    "with the -m option\n"), $Prog;
    234 		exit 1;
    235 	}
    236 
    237 	# Insure any specified directory exists, or apply a default.
    238 	if ($opt{d}) {
    239 		# User specified directory - make sure it exists.
    240 		if (! -d $opt{d}) {
    241 			printf STDERR gettext("%s: %s is not a directory\n"),
    242 			    $Prog, $opt{d};
    243 			exit 1;
    244 		}
    245 		$DestDir = $opt{d};
    246 	} else {
    247 		$DestDir = ".";
    248 	}
    249 
    250 	# Establish a temporary directory if necessary.
    251 	if (!$opt{D}) {
    252 		if (!($TmpDir = $ENV{TMPDIR}) || (! -d $TmpDir)) {
    253 			$TmpDir = "/tmp";
    254 		}
    255 	}
    256 
    257 	# Establish any initial ldd(1) argument requirements.
    258 	if ($LddArgs = $ENV{LARI_LDD_ARGS}) {
    259 		$LddArgs = $LddArgs . ' -r -e LD_DEBUG=bindings,files,detail';
    260 	} else {
    261 		$LddArgs = '-r -e LD_DEBUG=bindings,files,detail';
    262 	}
    263 
    264 	# If we've been asked to demangle symbols, make sure we can find the
    265 	# demangler.
    266 	if ($opt{C}) {
    267 		my ($DemName) = `dem XXXX 2> /dev/null`;
    268 		if (!$DemName) {
    269 			printf STDERR gettext("%s: can not locate demangler: " .
    270 			    "-C ignored\n"), $Prog;
    271 			$opt{C} = 0;
    272 		}
    273 	}
    274 
    275 	# If -a or -o hasn't been specified, default to -i.
    276 	if (!$opt{a} && !$opt{o}) {
    277 		$opt{i} = 1;
    278 	}
    279 
    280 	# Determine whether we have multiple input files.
    281 	if ($#ARGV == 0) {
    282 		$Mult = 0;
    283 	} else {
    284 		$Mult = 1;
    285 	}
    286 
    287 	# Determine what platform we're running on - some inappropriate
    288 	# platform specific dependencies are better skipped.
    289 	chomp($Platform = `uname -i`);
    290 
    291 	# Establish signal handlers
    292 	$SIG{INT} = \&Cleanup;
    293 	$SIG{QUIT} = \&Cleanup;
    294 
    295 	$DbgSeed = "";
    296 
    297 	# For each argument determine if we're dealing with a file or directory.
    298 	$Error = 0;
    299 	foreach my $Arg (@ARGV) {
    300 		if (!stat($Arg)) {
    301 			printf STDERR gettext("%s: %s: unable to stat file\n"),
    302 			    $Prog, $Arg;
    303 			$Error = 1;
    304 			next;
    305 		}
    306 
    307 		# Process simple files.
    308 		if (-f _) {
    309 			if (!-r _) {
    310 				printf STDERR gettext("%s: %s: unable to " .
    311 				   "read file\n"), $Prog, $Arg;
    312 				$Error = 1;
    313 				next;
    314 			}
    315 			if (!$opt{D}) {
    316 				if (ProcFile($Arg, $Mult, 1) == 0) {
    317 					$Error = 1;
    318 				}
    319 			} else {
    320 				# If the -D option is specified, read the
    321 				# bindings debugging information from the
    322 				# specified file.
    323 				if ($Mult) {
    324 					print STDOUT "$Arg:\n";
    325 				}
    326 				ProcBindings($Arg, $Mult, $Arg);
    327 			}
    328 			next;
    329 		}
    330 
    331 		# Process directories.
    332 		if (-d _) {
    333 			ProcDir($Arg);
    334 			next;
    335 		}
    336 
    337 		printf STDERR gettext("%s: %s: is not a file or directory\n"),
    338 		    $Prog, $Arg;
    339 		$Error = 1;
    340 	}
    341 	exit $Error;
    342 }
    343 
    344 sub ProcDir {
    345 	my ($Dir) = @_;
    346 	my ($File);
    347 
    348 	# Open the directory and read each entry, omit "." and "..".  Sorting
    349 	# the directory listing makes analyzing different source hierarchies
    350 	# easier.
    351 	if (opendir(DIR, $Dir)) {
    352 		foreach my $Entry (sort(readdir(DIR))) {
    353 			if (($Entry eq '.') || ($Entry eq '..')) {
    354 				next;
    355 			}
    356 
    357 			# If we're descending into a platform directory, ignore
    358 			# any inappropriate platform specific files.  These
    359 			# files can have dependencies that in turn bring in the
    360 			# appropriate platform specific file, resulting in more
    361 			# than one dependency offering the same interfaces.  In
    362 			# practice, the non-appropriate platform specific file
    363 			# wouldn't be loaded with a process.
    364 			if (($Dir =~ /\/platform$/) &&
    365 			    ($Entry !~ /^$Platform$/)) {
    366 				next;
    367 			}
    368 
    369 			$File = "$Dir/$Entry";
    370 			if (!lstat($File)) {
    371 				next;
    372 			}
    373 			# Ignore symlinks.
    374 			if (-l _) {
    375 				next;
    376 			}
    377 
    378 			# Descend into, and process any directories.
    379 			if (-d _) {
    380 				ProcDir($File);
    381 				next;
    382 			}
    383 			
    384 			# Process any standard files.
    385 			if (-f _ && -r _) {
    386 				ProcFile($File, 1, 0);
    387 				next;
    388 
    389 			}
    390 		}
    391 		closedir(DIR);
    392 	}
    393 }
    394 
    395 # Process a file.  If the file was explicitly defined on the command-line, and
    396 # an error occurs, tell the user.  Otherwise, this file probably came about from
    397 # scanning a directory, in which case just skip it and move on.
    398 sub ProcFile {
    399 	my ($File, $Mult, $CmdLine) = @_;
    400 	my (@Ldd, $NoFound, $DbgFile, @DbgGlob, $Type);
    401 
    402 	# If we're scanning a directory (ie. /lib) and have picked up ld.so.1,
    403 	# ignore it.
    404 	if (($CmdLine eq 0) && ($File =~ $Rtld)) {
    405 		return 1;
    406 	}
    407 
    408 	$Type = `LC_ALL=C file '$File' 2>&1`;
    409 	if (($Type !~ /dynamically linked/) || ($Type =~ /Sun demand paged/)) {
    410 		if ($CmdLine) {
    411 			printf STDERR gettext("%s: %s: is an invalid file " .
    412 			    "type\n"), $Prog, $File;
    413 		}
    414 		return 0;
    415 	}
    416 
    417 	# Create a temporary filename for capturing binding information.
    418 	$DbgSeed = basename($File);
    419 	$DbgSeed = "$TmpDir/lari.dbg.$$.$DbgSeed";
    420 
    421 	# Exercise the file under ldd(1), capturing all the bindings.
    422 	@Ldd = split(/\n/,
    423 	    `LC_ALL=C ldd $LddArgs -e LD_DEBUG_OUTPUT='$DbgSeed' '$File' 2>&1`);
    424 
    425 	# If ldd isn't -e capable we'll get a usage message.  The -e option was
    426 	# introduced in Solaris 9 and related patches.  Also, make sure the user
    427 	# sees any ldd errors.
    428 	$NoFound = 0;
    429 	for my $Line (@Ldd) {
    430 		if ($Line =~ /^usage: ldd/) {
    431 			printf STDERR gettext("%s: ldd: does not support -e, " .
    432 			    "unable to capture bindings output\n"), $Prog;
    433 			exit 1;
    434 		}
    435 		if ($Line =~ /not found/) {
    436 			$NoFound = 1;
    437 			last;
    438 		}
    439 	}
    440 
    441 	# The runtime linker will have appended a process id to the debug file.
    442 	# As we have to intuit the name, make sure there is only one debug
    443 	# file match, otherwise there must be some clutter in the output
    444 	# directory that is going to mess up our analysis.
    445 	foreach my $Match (<\Q${DbgSeed}\E.*>) {
    446 		if ($Match =~ /^\Q$DbgSeed\E\.\d+$/) {
    447 			push(@DbgGlob, $Match);
    448 		}
    449 	}
    450 	if (@DbgGlob == 0) {
    451 		# If there is no debug file, bail.  This can occur if the file
    452 		# being processed is secure.
    453 		if ($CmdLine) {
    454 			printf STDERR gettext("%s: %s: unable to capture " .
    455 			    "bindings output - possible secure application?\n"),
    456 			    $Prog, $File; 
    457 		}
    458 		return 0;
    459 	} elsif (@DbgGlob > 1) {
    460 		# Too many debug files found.
    461 		if ($CmdLine) {
    462 			printf STDERR gettext("%s: %s: multiple bindings " .
    463 			    "output files exist: %s: clean up temporary " .
    464 			    "directory\n"), $Prog, $File, $DbgSeed;
    465 		}
    466 		return 0;
    467 	} else {
    468 		$DbgFile = $DbgGlob[0];
    469 	}
    470 
    471 	# Ok, we're ready to process the bindings information.  Print a header
    472 	# if necessary, and if there were any ldd(1) errors push some of them
    473 	# out before any bindings information.  Limit the output, as it can
    474 	# sometimes be excessive.  If there are errors, the bindings information
    475 	# is likely to be incomplete.
    476 	if ($Mult) {
    477 		print STDOUT "$File:\n";
    478 	}
    479 	if ($NoFound) {
    480 		my ($Cnt) = 4;
    481 
    482 		for my $Line (@Ldd) {
    483 			if ($Line =~ /not found/) {
    484 				print STDOUT "$Line\n";
    485 				$Cnt--;
    486 			}
    487 			if ($Cnt == 0) {
    488 				print STDOUT gettext("\tcontinued ...\n");
    489 				last;
    490 			}
    491 		}
    492 	}
    493 
    494 	# If the user wants the original debugging file left behind, rename it
    495 	# so that it doesn't get re-read by another instance of lari processing
    496 	# this file.
    497 	if ($opt{s}) {
    498 		rename($DbgFile, $DbgSeed);
    499 		$DbgFile = $DbgSeed;
    500 		printf STDOUT gettext("%s: %s: bindings information " .
    501 		    "saved as: %s\n"), $Prog, $File, $DbgFile;
    502 	}
    503 
    504 	ProcBindings($File, $Mult, $DbgFile);
    505 
    506 	# Now that we've finished with the debugging file, nuke it if necessary.
    507 	if (!$opt{s}) {
    508 		unlink($DbgFile);
    509 	}
    510 	$DbgSeed = "";
    511 	return 1;
    512 }
    513 
    514 sub ProcBindings {
    515 	my ($File, $Mult, $DbgFile) = @_;
    516 	my (%Filtees, $FileHandle);
    517 
    518 	# Reinitialize our arrays when we're dealing with multiple files.
    519 	if ($Mult) {
    520 		%Symbols = ();
    521 		%Objects = ();
    522 		%Versioned = ();
    523 		%DemSyms = ();
    524 		%ObjFltrs = ();
    525 		%SymFltes = ();
    526 	}
    527 
    528 	# As debugging output can be significant, read a line at a time.
    529 	open($FileHandle, "<$DbgFile");
    530 	while (defined(my $Line = <$FileHandle>)) {
    531 		chomp($Line);
    532 
    533 		# Collect the symbols from any file analyzed.
    534 		if ($Line =~ /^.*: file=(.*);  analyzing .*/) {
    535 			GetAllSymbols($1);
    536 			next;
    537 		}
    538 
    539 		# Process any symbolic relocations that bind to a file.
    540 		if ($Line =~ /: binding file=.* to file=/) {
    541 			my ($RefFile, $DstFile, $SymName);
    542 			my (@Syms, $Found, @Fields);
    543 			my ($BndInfo) = 0;
    544 			my ($Offset) = 1;
    545 			my ($Dlsym) = 0;
    546 			my ($Detail) = 0;
    547 
    548 			# For greatest flexibility, split the line into fields
    549 			# and walk each field until we find what we need.
    550 			@Fields = split(' ', $Line);
    551 
    552 			# The referencing file, "... binding file=.* ".
    553 			while ($Fields[$Offset]) {
    554 				if ($Fields[$Offset] =~ /^file=(.*)/) {
    555 					$RefFile = $1;
    556 					$Offset++;
    557 					last;
    558 				}
    559 				$Offset++;
    560 			}
    561 			# The referencing offset, typically this is the address
    562 			# of the reference, "(0x1234...)", but in the case of a
    563 			# user lookup it's the string "(dlsym)".  If we don't
    564 			# find this offset information we've been given a debug
    565 			# file that didn't use the "detail" token, in which case
    566 			# we're not getting all the information we need.
    567 			if ($Fields[$Offset] =~ /^\((.*)\)/) {
    568 				if ($1 eq 'dlsym') {
    569 					$Dlsym = 1;
    570 				}
    571 				$Detail = 1;
    572 				$Offset++;
    573 			}
    574 			# The destination file, "... to file=.* ".  Note, in the
    575 			# case of a rejection message, the file is terminated
    576 			# with a colon, "... to file=.*: ", which must be
    577 			# removed
    578 			while ($Fields[$Offset]) {
    579 				if ($Fields[$Offset] =~ /^file=(.*)/) {
    580 					$DstFile = $1;
    581 					$DstFile =~ s/:$//;
    582 					$Offset++;
    583 					last;
    584 				}
    585 				$Offset++;
    586 			}
    587 			# The symbol being bound, "... symbol `.*' ...".
    588 			while ($Fields[$Offset]) {
    589 				if ($Fields[$Offset] =~ /^\`(.*)\'$/) {
    590 					$SymName = $1;
    591 					$Offset++;
    592 					last;
    593 				}
    594 				$Offset++;
    595 			}
    596 			# Possible trailing binding info, "... (direct,...", or
    597 			# a rejection, "... (rejected - ...".
    598 			while ($Fields[$Offset]) {
    599 				if ($Fields[$Offset] =~ /^\((.*)/) {
    600 					$BndInfo = $1;
    601 					$Detail = 1;
    602 					$Offset++;
    603 					last;
    604 				}
    605 				$Offset++;
    606 			}
    607 
    608 			if ($Detail == 0) {
    609 				printf STDERR gettext("%s: %s: debug file " .
    610 				    "does not contain `detail' information\n"),
    611 				    $Prog, $DbgFile;
    612 				return;
    613 			}
    614 
    615 			# Collect the symbols from each object.
    616 			GetAllSymbols($RefFile);
    617 			GetAllSymbols($DstFile);
    618 
    619 			# Identify that this definition has been bound to.
    620 			$Symbols{$SymName}{$DstFile}[$ObjRef]++;
    621 			if ($RefFile eq $DstFile) {
    622 				# If the reference binds to a definition within
    623 				# the same file this symbol may be a candidate
    624 				# for reducing to local.
    625 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Self;
    626 				$Objects{$DstFile}{$SymName} |= $Self;
    627 			} else {
    628 				# This symbol is required to satisfy an external
    629 				# reference.
    630 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Extn;
    631 				$Objects{$DstFile}{$SymName} |= $Extn;
    632 			}
    633 
    634 			# Assign any other state indicated by the binding info
    635 			# associated with the diagnostic output.
    636 			if (!$BndInfo) {
    637 				next;
    638 			}
    639 
    640 			if ($BndInfo =~ /direct/) {
    641 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Dirc;
    642 				$Objects{$DstFile}{$SymName} |= $Dirc;
    643 			}
    644 			if ($BndInfo =~ /copy-ref/) {
    645 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Cpyr;
    646 				$Objects{$DstFile}{$SymName} |= $Cpyr;
    647 			}
    648 			if ($BndInfo =~ /filtee/) {
    649 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Gfte;
    650 				$Objects{$DstFile}{$SymName} |= $Gfte;
    651 			}
    652 			if ($BndInfo =~ /interpose/) {
    653 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Intp;
    654 				$Objects{$DstFile}{$SymName} |= $Intp;
    655 			}
    656 			if ($BndInfo =~ /plt-addr/) {
    657 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Plta;
    658 				$Objects{$DstFile}{$SymName} |= $Plta;
    659 			}
    660 			if ($BndInfo =~ /rejected/) {
    661 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Rejt;
    662 				$Objects{$DstFile}{$SymName} |= $Rejt;
    663 			}
    664 			if ($Dlsym) {
    665 				$Symbols{$SymName}{$DstFile}[$ObjFlag] |= $User;
    666 				$Objects{$DstFile}{$SymName} |= $User;
    667 			}
    668 		}
    669 	}
    670 	close($FileHandle);
    671 
    672 	# Now that we've processed all objects, traverse the set of object
    673 	# filters that have been captured from parsing any FILTER and AUXILIARY
    674 	# dynamic tags.  For each filtee, determine which of the symbols it
    675 	# exports are also defined in the filter.  If a filter is bound to, the
    676 	# runtime linkers diagnostics will indicate a filtee binding.  However,
    677 	# some of the filtee symbols may not be bound to, so here we mark them
    678 	# all so as to remove them from any interesting output.
    679 	for my $Filter (keys(%ObjFltrs)) {
    680 
    681 		# Determine the filtees that are associated with this filter.
    682 		for my $Filtee (keys(%{$ObjFltrs{$Filter}})) {
    683 			my ($FileName);
    684 
    685 			# Reduce the filtee to a simple file name.  Then, try
    686 			# and associate this simple file name with the objects
    687 			# that have been processed.  These objects are typically
    688 			# recorded with a full path name.
    689 			chomp($FileName = `basename $Filtee`);
    690 			for my $Obj (keys(%Objects)) {
    691 				if ($Obj =~ /\/$FileName$/) {
    692 					$Filtee = $Obj;
    693 					last;
    694 				}
    695 			}
    696 
    697 			if (!exists($Objects{$Filtee})) {
    698 				next;
    699 			}
    700 
    701 			# Traverse the symbols of the filtee (these are
    702 			# typically a smaller set than the filter) and if the
    703 			# symbol is defined by the filter tag the symbol as a
    704 			# filtee.
    705 			for my $SymName (keys(%{$Objects{$Filtee}})) {
    706 				my ($OFlag, $FFlag);
    707 
    708 				# Ignore the usual stuff.
    709 				if (($SymName =~ $MultSyms) ||
    710 				    ($SymName =~ $CrtSyms)) {
    711 					next;
    712 				}
    713 
    714 				if (!$Symbols{$SymName}{$Filter}) {
    715 					next;
    716 				}
    717 
    718 				# Determine the type of filter.
    719 				$OFlag = $Symbols{$SymName}{$Filter}[$ObjFlag];
    720 
    721 				# Specifically identify the type of filtee we
    722 				# have and remove any generic filtee flag.
    723 				if ($OFlag & ($Osft | $Ssft)) {
    724 					$FFlag = $Sfte;
    725 				} else {
    726 					$FFlag = $Afte;
    727 				}
    728 
    729 				$Symbols{$SymName}{$Filtee}[$ObjFlag] |= $FFlag;
    730 				$Symbols{$SymName}{$Filtee}[$ObjFlag] &= ~$Gfte;
    731 			}
    732 		}
    733 	}
    734 
    735 	# Traverse the set of per-symbol filters making sure we've tagged any
    736 	# associated filtee symbols, as we did above for object filters.
    737 	for my $Filtee (keys(%SymFltes)) {
    738 		my ($FullPath) = $Filtee;
    739 		my ($FileName);
    740 
    741 		# Reduce the filtee to a simple file name.  Then, try and
    742 		# associate this simple file name with the objects that have
    743 		# been processed.  These objects are typically recorded with a
    744 		# full path name.
    745 		chomp($FileName = `basename $Filtee`);
    746 		for my $Obj (keys(%Objects)) {
    747 			if ($Obj =~ /\/$FileName$/) {
    748 				$FullPath = $Obj;
    749 				last;
    750 			}
    751 		}
    752 
    753 		if (!exists($Objects{$FullPath})) {
    754 			next;
    755 		}
    756 
    757 		for my $SymName (keys(%{$SymFltes{$Filtee}})) {
    758 			my ($OFlag, $FFlag);
    759 
    760 			# Determine the type of filter.
    761 			$OFlag = $SymFltes{$Filtee}{$SymName}[$SymFlag];
    762 
    763 			# Specifically identify the type of filtee we have and
    764 			# remove any generic filtee flag.
    765 			if ($OFlag & $Ssft) {
    766 				$FFlag = $Sfte;
    767 			} else {
    768 				$FFlag = $Afte;
    769 			}
    770 
    771 			$Symbols{$SymName}{$FullPath}[$ObjFlag] |= $FFlag;
    772 			$Symbols{$SymName}{$FullPath}[$ObjFlag] &= ~$Gfte;
    773 		}
    774 	}
    775 
    776 	# Process objects and their symbols as required.
    777 	if ($opt{m}) {
    778 		# If we're creating a mapfile, traverse each object we've
    779 		# collected.
    780 		foreach my $Obj (keys(%Objects)) {
    781 			my ($File, $Path);
    782 
    783 			# Skip any objects that should be ignored.
    784 			if ($Obj =~ $Rtld) {
    785 				next;
    786 			}
    787 
    788 			# Skip any versioned objects if required.
    789 			if ($opt{v} && $Versioned{$Obj}) {
    790 				next;
    791 			}
    792 
    793 			# Open the mapfile if required.
    794 			$File = basename($Obj);
    795 			$Path = "$DestDir/mapfile-$File";
    796 			if (!open(MAPOUT, "> $Path")) {
    797 				printf STDERR gettext("%s: %s: open failed:" .
    798 				    "%s\n"), $Prog, $Path, $!;
    799 				exit 1;
    800 			}
    801 
    802 			# Establish the mapfile preamble.
    803 			print MAPOUT "#\n# Interface Definition mapfile for:\n";
    804 			print MAPOUT "#\tDynamic Object: $Obj\n";
    805 			print MAPOUT "#\tProcess:        $File\n#\n\n";
    806 
    807 			# Process each global symbol.
    808 			print MAPOUT "$File {\n\tglobal:\n";
    809 
    810 			foreach my $SymName (sort(keys(%{$Objects{$Obj}}))) {
    811 				my ($Flag) = $Objects{$Obj}{$SymName};
    812 
    813 				# For the first pass we're only interested in
    814 				# symbols that have been bound to from an
    815 				# external object, or must be global to enable
    816 				# a binding to an interposing definition.
    817 				# Skip bindings to ourself, as these are
    818 				# candidates for demoting to local.
    819 				if (!($Flag & ($Extn | $Intp))) {
    820 					next;
    821 				}
    822 				if (($Flag & ($Extn | $Self)) == $Self) {
    823 					next;
    824 				}
    825 
    826 				# Add the demangled name as a comment if
    827 				# required.
    828 				if ($opt{C}) {
    829 					my ($DemName) = Demangle($SymName);
    830 
    831 					if ($DemName ne "") {
    832 						print MAPOUT "\t\t#$DemName\n";
    833 					}
    834 				}
    835 				print MAPOUT "\t\t$SymName;\n";
    836 			}
    837 
    838 			# Process each local demotion.
    839 			print MAPOUT "\tlocal:\n";
    840 
    841 			if ($opt{o}) {
    842 				foreach my $SymName
    843 				    (sort(keys(%{$Objects{$Obj}}))) {
    844 					my ($Flag) = $Objects{$Obj}{$SymName};
    845 
    846 					# For this pass we're only interested
    847 					# in symbol definitions that haven't
    848 					# been bound to, or have only been
    849 					# bound to from the same object.
    850 					if ($Flag & $Extn) {
    851 						next;
    852 					}
    853 
    854 					# Add the demangled name as a comment if
    855 					# required.
    856 					if ($opt{C}) {
    857 						my ($DemName) =
    858 						    Demangle($SymName);
    859 
    860 						if ($DemName ne "") {
    861 							print MAPOUT
    862 							    "\t\t#$DemName\n";
    863 						}
    864 					}
    865 					print MAPOUT "\t\t$SymName;\n";
    866 				}
    867 			}
    868 
    869 			# Capture everything else as local.
    870 			print MAPOUT "\t\t\*;\n};\n";
    871 			close MAPOUT;
    872 		}
    873 
    874 	} else {
    875 		# If we're gathering information regarding the symbols used by
    876 		# the process, automatically sort any standard output using the
    877 		# symbol name.
    878 		if (!open(SORT, "| sort +1")) {
    879 			printf STDERR gettext("%s: fork failed: %s\n"),
    880 			    $Prog, $!;
    881 			exit 1;
    882 		}
    883 
    884 		foreach my $SymName (keys(%Symbols)) {
    885 			my ($Cnt);
    886 			
    887 			# If we're looking for interesting symbols, inspect
    888 			# each definition of each symbol.  If one is found to
    889 			# be interesting, the whole family are printed.
    890 			if (($Cnt = Interesting($SymName)) == 0) {
    891 				next;
    892 			}
    893 
    894 			# We've found something interesting, or all symbols
    895 			# should be output.  List all objects that define this
    896 			# symbol.
    897 			foreach my $Obj (keys(%{$Symbols{$SymName}})) {
    898 				my ($DemName, $Type);
    899 				my ($Flag) = $Symbols{$SymName}{$Obj}[$ObjFlag];
    900 				my ($Str) = "$Cnt:";
    901 				my ($Vis);
    902 				my ($DisVis) = "";
    903 
    904 				# Do we just want overhead symbols.  Consider
    905 				# copy-relocations, rejections, and plt address
    906 				# binding, as overhead too.
    907 				if ($opt{o} && (($Flag &
    908 				    ($Rejt | $Extn | $Cpyr | $Plta)) == $Extn)) {
    909 					next;
    910 				}
    911 
    912 				# Do we just want all symbols that have been
    913 				# bound to.
    914 				if (($opt{a} || $opt{o}) && $opt{b} &&
    915 				    (($Flag & ($Extn | $Self | $Prot)) == 0)) {
    916 					next;
    917 				}
    918 
    919 				# If we haven't been asked for all symbols, only
    920 				# print those reserved symbols that have been
    921 				# bound to, as the number of reserved symbols
    922 				# can be quite excessive.  Also, remove any
    923 				# standard filters, as nothing can bind to these
    924 				# symbols anyway, provided they have not
    925 				# contributed to a rejected binding.
    926 				if (!$opt{a} && ((($SymName =~ $MultSyms) &&
    927 				    (($Flag & ($Extn | $Self)) == 0)) ||
    928 				    (($SymName =~ $CrtSyms) && (($Flag &
    929 				    ($Extn | $Self | $Prot)) == 0)) ||
    930 				    (($Flag & ($Ssft | $Osft)) &&
    931 				    (($Flag & $Rejt) == 0)))) {
    932 					next;
    933 				}
    934 
    935 				# Skip any versioned objects if required.
    936 				if ($opt{v} && $Versioned{$Obj}) {
    937 					next;
    938 				}
    939 
    940 				# Display this symbol.
    941 				if ($Symbols{$SymName}{$Obj}[$ObjRef]) {
    942 					$Str = $Str . 
    943 					    $Symbols{$SymName}{$Obj}[$ObjRef];
    944 				} else {
    945 					$Str = $Str . '0';
    946 				}
    947 
    948 				# Has the symbol been bound to externally
    949 				if ($Flag & $Extn) {
    950 					$Str = $Str . 'E';
    951 				}
    952 				# Has the symbol been bound to from the same
    953 				# object.
    954 				if ($Flag & $Self) {
    955 					$Str = $Str . 'S';
    956 				}
    957 				# Has the symbol been bound to directly.
    958 				if ($Flag & $Dirc) {
    959 					$Str = $Str . 'D';
    960 				}
    961 				# Does this symbol originate for an explicit
    962 				# interposer.
    963 				if ($Flag & $Intp) {
    964 					$Str = $Str . 'I';
    965 				}
    966 				# Is this symbol the reference data of a copy
    967 				# relocation.
    968 				if ($Flag & $Cpyr) {
    969 					$Str = $Str . 'C';
    970 				}
    971 				# Is this symbol part of filtee.
    972 				if ($Flag & ($Sfte | $Afte | $Gfte)) {
    973 					$Str = $Str . 'F';
    974 				}
    975 				# Is this symbol protected (in which case there
    976 				# may be a symbolic binding within the same
    977 				# object to this symbol).
    978 				if ($Flag & $Prot) {
    979 					$Str = $Str . 'P';
    980 				}
    981 				# Is this symbol an executables .plt address.
    982 				if ($Flag & $Plta) {
    983 					$Str = $Str . 'A';
    984 				}
    985 				# Does this binding originate from a user
    986 				# (dlsym) request.
    987 				if ($Flag & $User) {
    988 					$Str = $Str . 'U';
    989 				}
    990 				# Does this definition redirect the binding.
    991 				if ($Flag & $Msft) {
    992 					$Str = $Str . 'R';
    993 				}
    994 				# Does this definition explicitly define no
    995 				# direct binding.
    996 				if ($Flag & $Nodi) {
    997 					$Str = $Str . 'N';
    998 				}
    999 				# Was a binding to this definition rejected at
   1000 				# some point.
   1001 				if ($Flag & $Rejt) {
   1002 					$Str = $Str . 'r';
   1003 				}
   1004 
   1005 				# Determine whether this is a function or a data
   1006 				# object.  For the latter, display the symbol
   1007 				# size.  Otherwise, the symbol is a reserved
   1008 				# label, and is left untyped.
   1009 				if ($Flag & $Func) {
   1010 					$Type = '()';
   1011 				} elsif ($Flag & $Objt) {
   1012 					$Type = '[' .
   1013 					    $Symbols{$SymName}{$Obj}[$ObjSize] .
   1014 					']';
   1015 				} else {
   1016 					$Type = "";
   1017 				}
   1018 
   1019 				# Demangle the symbol name if desired.
   1020 				$DemName = Demangle($SymName);
   1021 
   1022 				# If symbol visibility differences are
   1023 				# interesting, append the verbose representation
   1024 				# of any interesting visibilities.
   1025 				$Vis = $Symbols{$SymName}{$Obj}[$ObjVis];
   1026 				if ($opt{V} && $Vis) {
   1027 					if ($Vis =~ 'S') {
   1028 						$DisVis = "  (singleton)";
   1029 					} elsif ($Vis =~ 'P') {
   1030 						$DisVis = "  (protected)";
   1031 					}
   1032 				}
   1033 				if ($Mult) {
   1034 					print SORT "  [$Str]: " .
   1035 					    "$SymName$Type$DemName: " .
   1036 					    "$Obj$DisVis\n";
   1037 				} else {
   1038 					print SORT "[$Str]: " .
   1039 					    "$SymName$Type$DemName: " .
   1040 					    "$Obj$DisVis\n";
   1041 				}
   1042 			}
   1043 		}
   1044 		close SORT;
   1045 	}
   1046 }
   1047 
   1048 # Heuristics to determine whether a symbol binding is interesting.  In most
   1049 # applications there can be a large amount of symbol binding information to
   1050 # wade through.  The most typical binding, to a single definition, probably
   1051 # isn't interesting or the cause of unexpected behavior.  Here, we try and
   1052 # determine those bindings that may can cause unexpected behavior.
   1053 #
   1054 # Note, this routine is actually called for all symbols so that their count
   1055 # can be calculated in one place.
   1056 sub Interesting
   1057 {
   1058 	my ($SymName) = @_;
   1059 	my ($ObjCnt, $GFlags, $BndCnt, $FltCnt, $NodiCnt, $RdirCnt, $ExRef);
   1060 	my ($RejCnt, $TotCnt);
   1061 
   1062 	# Scan all definitions of this symbol, thus determining the definition
   1063 	# count, the number of filters, redirections, executable references
   1064 	# (copy-relocations, or plt addresses), no-direct bindings, and the
   1065 	# number of definitions that have been bound to.
   1066 	$ObjCnt = $GFlags = $BndCnt = $FltCnt =
   1067 	    $NodiCnt = $RdirCnt = $ExRef = $RejCnt = $TotCnt = 0;
   1068 	foreach my $Obj (keys(%{$Symbols{$SymName}})) {
   1069 		my ($Flag) = $Symbols{$SymName}{$Obj}[$ObjFlag];
   1070 
   1071 		$TotCnt++;
   1072 
   1073 		# Ignore standard filters when determining the symbol count, as
   1074 		# a standard filter can never be bound to.
   1075 		if (($Flag & ($Osft | $Ssft)) == 0) {
   1076 			$ObjCnt++;
   1077 		}
   1078 
   1079 		# If we're only looking at interesting objects, then standard
   1080 		# filters are ignored, so suppress any standard filtee tagging.
   1081 		if (!$opt{a}) {
   1082 			$Flag = $Symbols{$SymName}{$Obj}[$ObjFlag] &= ~$Sfte;
   1083 		}
   1084 
   1085 		$GFlags |= $Flag;
   1086 		if ($Flag & ($Sfte | $Afte | $Gfte)) {
   1087 			$FltCnt++;
   1088 		}
   1089 		if ($Flag & $Nodi) {
   1090 			$NodiCnt++;
   1091 		}
   1092 		if ($Flag & ($Cpyr | $Plta)) {
   1093 			$ExRef++;
   1094 		}
   1095 		if ($Flag & $Msft) {
   1096 			$RdirCnt++;
   1097 		}
   1098 		if ($Flag & $Rejt) {
   1099 			$RejCnt++;
   1100 		}
   1101 
   1102 		# Ignore bindings to undefined .plts, and copy-relocation
   1103 		# references.  These are implementation details, rather than
   1104 		# a truly interesting multiple-binding.  If a symbol is tagged
   1105 		# as protected, count it as having bound to itself, even though
   1106 		# we can't tell if it's really been used.
   1107 		if (($Flag & ($Self | $Extn | $Prot)) &&
   1108 		    (($Flag & ($Plta | $Cpyr)) == 0)) {
   1109 			$BndCnt++;
   1110 		}
   1111 	}
   1112 
   1113 	# If we want all overhead symbols, return the count.
   1114 	if ($opt{o}) {
   1115 		return $ObjCnt;
   1116 	}
   1117 
   1118 	# If we want all symbols, return the count.  If we want all bound
   1119 	# symbols, return the count provided it is non-zero.
   1120 	if ($opt{a} && (!$opt{b} || ($BndCnt > 0))) {
   1121 		return $TotCnt;
   1122 	}
   1123 
   1124 	# Any rejected symbol is interesting
   1125 	if ($RejCnt) {
   1126 		return $TotCnt;
   1127 	}
   1128 
   1129 	# Single instance symbol definitions aren't very interesting.
   1130 	if ($ObjCnt == 1) {
   1131 		return 0;
   1132 	}
   1133 
   1134 	# Traverse each symbol definition looking for the following:
   1135 	#
   1136 	#   .	Multiple symbols are bound to externally.
   1137 	#   .	A symbol is bound to externally, and possibly symbolically.
   1138 	#
   1139 	# Two symbol bindings are acceptable in some cases, and thus aren't
   1140 	# interesting:
   1141 	#
   1142 	#   .	Copy relocations.  Here, the executable binds to a shared object
   1143 	#	to access the data definition, which is then copied to the
   1144 	#	executable.  All other references should then bind to the copied
   1145 	#	data.
   1146 	#   .	Non-plt relocations to functions that are referenced by the
   1147 	#	executable will bind to the .plt in the executable.  This
   1148 	#	provides for address comparison calculations (although plainly
   1149 	#	an overhead).
   1150 	#
   1151 	# Multiple symbol bindings are acceptable in some cases, and thus aren't
   1152 	# interesting:
   1153 	#
   1154 	#   .	Filtees.  Multiple filtees may exist for one filter.
   1155 	#
   1156 	if ((($ObjCnt == 2) && ($GFlags & ($Cpyr | $Plta))) ||
   1157 	    ($ObjCnt == ($FltCnt + 1))) {
   1158 		return 0;
   1159 	}
   1160 
   1161 	# Only display any reserved symbols if more than one binding has
   1162 	# occurred.
   1163 	if ((($SymName =~ $MultSyms) || ($SymName =~ $CrtSyms)) &&
   1164 	    ($BndCnt < 2)) {
   1165 		return (0);
   1166 	}
   1167 
   1168 	# For all other symbols, determine whether a binding has occurred.
   1169 	# Note: definitions within an executable are tagged as protected ("P")
   1170 	# as they may have been bound to from within the executable - we can't
   1171 	# tell.
   1172 	if ($opt{b} && ($BndCnt == 0)) {
   1173 		return (0);
   1174 	}
   1175 
   1176 	# Multiple instances of a definition, where all but one are filter
   1177 	# references and/or copy relocations, are also uninteresting.
   1178 	# Effectively, only one symbol is providing the final binding.
   1179 	if (($FltCnt && $RdirCnt) &&
   1180 	    (($FltCnt + $RdirCnt + $ExRef) == $ObjCnt)) {
   1181 		return (0);
   1182 	}
   1183 
   1184 	# Multiple instances of explicitly defined no-direct binding symbols
   1185 	# are known to occur, and their no-binding definition indicates they
   1186 	# are expected and accounted for.  Thus, these aren't interesting.
   1187 	if (($ExRef + $NodiCnt) == $ObjCnt) {
   1188 		return (0);
   1189 	}
   1190 
   1191 	# We have an interesting symbol, returns its count.
   1192 	return $ObjCnt;
   1193 }
   1194 
   1195 # Obtain the global symbol definitions of an object and determine whether the
   1196 # object has been versioned.
   1197 sub GetAllSymbols {
   1198 	my ($Obj) = @_;
   1199 	my ($Type, $FileHandle);
   1200 	my (%AddrToName, %NameToAddr);
   1201 	my ($Exec) = 0;
   1202 	my ($Symb) = 0;
   1203 	my ($Copy) = 0;
   1204 	my ($Interpose) = 0;
   1205 	my ($Fltr) = 0;
   1206 	my ($Ehdr) = 0;
   1207 	my ($Dyn) = 0;
   1208 	my ($Rel) = 0;
   1209 	my ($Info) = 0;
   1210 
   1211 	# Determine whether we've already retrieved this object's symbols.
   1212 	# Also, ignore the runtime linker, it's on a separate link-map, and
   1213 	# except for the filtee symbols that might be bound via libdl, is
   1214 	# uninteresting.  Tag the runtime linker as versioned to simplify
   1215 	# possible -v processing.
   1216 	if ($Objects{$Obj}) {
   1217 		return;
   1218 	}
   1219 
   1220 	if ($Obj =~ $Rtld) {
   1221 		$Versioned{$Obj} = 1;
   1222 		return;
   1223 	}
   1224 
   1225 	# Get as much ELF information as we can from elfdump(1).  A second
   1226 	# invocation of elfdump(1) is required to obtain the symbol table, whose
   1227 	# processing can be affected by states determined during this pass.
   1228 	#
   1229 	# The information required:
   1230 	#	-e	ELF header provides the file type
   1231 	#	-d	dynamic information provides filter names 
   1232 	#	-r	relocations provide for copy relocations
   1233 	#	-v	object versioning
   1234 	#	-y	symbol information section provide pre-symbol filters
   1235 	#		and direct binding information
   1236 	#
   1237 	# As this information can be quite large, process the elfdump(1) output
   1238 	# through a pipe.
   1239 	open($FileHandle, "LC_ALL=C elfdump -edrvy '$Obj' 2> /dev/null |");
   1240 
   1241 	while (defined(my $Line = <$FileHandle>)) {
   1242 		my (@Fields);
   1243 
   1244 		chomp($Line);
   1245 
   1246 		# Each collection of data is preceded with a title that
   1247 		# starts in column 0.  Items of data all have some form of
   1248 		# indentation.
   1249 		if ($Line =~ /^[A-Z]/) {
   1250 			if ($Line =~ /^ELF Header/) {
   1251 				$Ehdr = 1;
   1252 				$Dyn = $Rel = $Info = 0;
   1253 			} elsif ($Line =~ /^Dynamic Section:/) {
   1254 				$Dyn = 1;
   1255 				$Ehdr = $Rel = $Info = 0;
   1256 			} elsif ($Line =~ /^Relocation Section:/) {
   1257 				$Rel = 1;
   1258 				$Ehdr = $Dyn = $Info = 0;
   1259 			} elsif ($Line =~ /^Syminfo Section:/) {
   1260 				$Info = 1;
   1261 				$Ehdr = $Dyn = $Rel = 0;
   1262 			} elsif ($Line =~ /^Version Definition Section:/) {
   1263 				# The existance of a VERDEF section is all we
   1264 				# are looking for. There is no need to parse
   1265 				# the specific version definitions.
   1266 				$Versioned{$Obj} = 1;
   1267 			    	$Ehdr = $Dyn = $Rel = $Info = 0;
   1268 			} else {
   1269 				$Ehdr = $Dyn = $Rel = $Info = 0;
   1270 			}
   1271 			next;
   1272 		}
   1273 
   1274 		# Inspect the ELF header.
   1275 		if ($Ehdr eq 1) {
   1276 			# Determine the ELF file type from the e_type element.
   1277 			if ($Line =~ /e_type:/) {
   1278 				if ($Line =~ /ET_EXEC/) {
   1279 					$Exec = 1;
   1280 				}
   1281 
   1282 				# There's nothing of interest left in the ELF
   1283 				# header, so skip processing other entries.
   1284 				$Ehdr = 0;
   1285 				next;
   1286 			}
   1287 		}
   1288 
   1289 		# Inspect the .dynamic section.
   1290 		if ($Dyn eq 1) {
   1291 			@Fields = split(' ', $Line);
   1292 
   1293 			# Determine if the FILTER or AUXILIARY tag is set.
   1294 			if ($#Fields == 3) {
   1295 				my ($Flte) = 0;
   1296 
   1297 				if ($Fields[1] eq 'FILTER') {
   1298 					$Fltr |= $Osft;
   1299 					$Flte = 1;
   1300 				}
   1301 				elsif ($Fields[1] eq 'AUXILIARY') {
   1302 					$Fltr |= $Oaft;
   1303 					$Flte = 1;
   1304 				}
   1305 				if ($Flte eq 1) {
   1306 					my (@Filtees) = split(':', $Fields[3]);
   1307 
   1308 					for my $Filtee (@Filtees) {
   1309 						if ($Filtee =~ $Rtld) {
   1310 							next;
   1311 						}
   1312 						$ObjFltrs{$Obj}{$Filtee} = 1;
   1313 					}
   1314 				}
   1315 				next;
   1316 			}
   1317 
   1318 			# We're only interested in the FLAGS entry.
   1319 			if (($#Fields < 4) || ($Fields[1] !~ /^FLAGS/)) {
   1320 				next;
   1321 			}
   1322 
   1323 			# Determine whether we've got a symbolicly bound object.
   1324 			# With newer link-editors, all symbols will be marked as
   1325 			# protected ("P"), but with older link-editors this
   1326 			# state could only be inferred from the symbolic dynamic
   1327 			# tag.
   1328 			if (($Fields[1] eq 'FLAGS') &&
   1329 			    ($Line =~ / SYMBOLIC /)) {
   1330 				$Symb = 1;
   1331 				next;
   1332 			}
   1333 
   1334 			# Determine whether this object is an interposer.
   1335 			if (($Fields[1] eq 'FLAGS_1') &&
   1336 			    ($Line =~ / OBJECT-INTERPOSE /)) {
   1337 				$Interpose = 1;
   1338 				next;
   1339 			}
   1340 			next;
   1341 		}
   1342 
   1343 		# Inspect the relocation information.  As we're only looking
   1344 		# for copy relocations, this processing is only necessary for
   1345 		# executables.
   1346 		if ($Rel eq 1) {
   1347 			my ($SymName);
   1348 
   1349 			if ($Exec eq 0) {
   1350 				$Rel = 0;
   1351 				next;
   1352 			}
   1353 
   1354 			# Obtain any copy relocations.
   1355 			if ($Line !~ / R_[A-Z0-9]+_COPY /) {
   1356 				next;
   1357 			}
   1358 
   1359 			@Fields = split(' ', $Line);
   1360 
   1361 			# Intel relocation records don't contain an addend,
   1362 			# where as every other supported platform does.
   1363 			if ($Fields[0] eq 'R_386_COPY') {
   1364 				$SymName = $Fields[3];
   1365 			} else {
   1366 				$SymName = $Fields[4];
   1367 			}
   1368 
   1369 			$Symbols{$SymName}{$Obj}[$ObjFlag] |= $Cpyr;
   1370 			$Objects{$Obj}{$SymName} |= $Cpyr;
   1371 			$Copy = 1;
   1372 		}
   1373 
   1374 		# Inspect the .SUNW_syminfo section.
   1375 		if ($Info eq 1) {
   1376 			my ($SymName);
   1377 			my ($Flags) = 0;
   1378 
   1379 			@Fields = split(' ', $Line);
   1380 
   1381 			# Binding attributes are in the second column.
   1382 			if ($#Fields < 1) {
   1383 				next;
   1384 			}
   1385 			if ($Fields[1] =~ /N/) {
   1386 				$Flags |= $Nodi;
   1387 			}
   1388 			if ($Fields[1] =~ /F/) {
   1389 				$Flags |= $Ssft;
   1390 			}
   1391 			if ($Fields[1] =~ /A/) {
   1392 				$Flags |= $Saft;
   1393 			}
   1394 			if ($Fields[1] =~ /I/) {
   1395 				$Flags |= $Intp;
   1396 			}
   1397 
   1398 			# Determine the symbol name based upon the number of
   1399 			# fields.
   1400 			if ($Flags) {
   1401 				$SymName = $Fields[$#Fields];
   1402 				$Symbols{$SymName}{$Obj}[$ObjFlag] |= $Flags;
   1403 				$Objects{$Obj}{$SymName} |= $Flags;
   1404 			}
   1405 
   1406 			# If this is a filter, we need to tag the associated
   1407 			# filtee symbol.  However, the filtee might not have
   1408 			# been processed yet, so save this information for later.
   1409 			$Flags &= ~($Nodi | $Intp);
   1410 			if ($Flags) {
   1411 				my ($Filtee) = $Fields[$#Fields - 1];
   1412 
   1413 				if ($Filtee =~ $Rtld) {
   1414 					next;
   1415 				}
   1416 				$SymFltes{$Filtee}{$SymName}[$SymFlag] = $Flags;
   1417 			}
   1418 		}
   1419 	}
   1420 
   1421 	close($FileHandle);
   1422 
   1423 	# If there's no expected information, it's possible we've been given a
   1424 	# debug output file and are processing the file from a location from
   1425 	# which the dependencies specified in the debug file aren't accessible.
   1426 	if ($Dyn eq 0) {
   1427 		printf STDERR gettext("%s: %s: unable to process ELF file\n"),
   1428 		    $Prog, $Obj;
   1429 
   1430 		# Add the file to our list, so that we don't create the same
   1431 		# message again.  Processing should continue so that we can
   1432 		# flush out as many error messages as possible.
   1433 		$Objects{$Obj}{"DoesNotExist"} = 0;
   1434 		return;
   1435 	}
   1436 
   1437 	# Process elfdump(1) once more to obtain the .dynsym symbol table. We
   1438 	# are only interested in global symbols, so .SUNW_ldynsym is not needed.
   1439 	open($FileHandle, "LC_ALL=C elfdump -sN.dynsym '$Obj' 2> /dev/null |");
   1440 
   1441 	while (defined(my $Line = <$FileHandle>)) {
   1442 		chomp($Line);
   1443 
   1444 		my (@Fields) = split(' ', $Line);
   1445 		my ($Flags);
   1446 
   1447 		# We're only interested in defined symbol entries.  Unless
   1448 		# we've been asked for all symbols, ignore any ABS or NOTY
   1449 		# symbols.  The former are typically reserved symbols or
   1450 		# versioning names.  The latter are labels that are not bound
   1451 		# to.  Note, ABS and NOTY symbols of non-zero size have been
   1452 		# known to occur, so capture them.
   1453 		if (($#Fields < 8) || ($Fields[4] !~ $GlobWeak) ||
   1454 		    ($Fields[7] eq 'UNDEF') ||
   1455 		    (!$opt{a} && (oct($Fields[2]) eq 0) &&
   1456 		    ((($Fields[7] eq 'ABS') && ($Fields[3] eq 'OBJT')) ||
   1457 		    ($Fields[3] eq 'NOTY')))) {
   1458 			next;
   1459 		}
   1460 
   1461 		# If we're found copy relocations, save the address of all OBJT
   1462 		# definitions, together with the copy symbol.  These definitions
   1463 		# are used to determine whether the copy symbol has any aliases
   1464 		# (ie. __iob and _iob).
   1465 		if (($Copy eq 1) && ($Fields[3] eq 'OBJT')) {
   1466 			push(@{$AddrToName{$Fields[1]}}, $Fields[8]);
   1467 
   1468 			if (($Symbols{$Fields[8]}{$Obj}) &&
   1469 			    ($Symbols{$Fields[8]}{$Obj}[$ObjFlag] & $Cpyr)) {
   1470 				$NameToAddr{$Fields[8]} = $Fields[1];
   1471 			}
   1472 		}
   1473 
   1474 		# Identify this symbol as global, and associate it with any
   1475 		# object filtering.
   1476 		$Flags = $Glob | $Fltr;
   1477 
   1478 		# If the symbol visibility is protected, this is an internal
   1479 		# symbolic binding.  Note, an INTERNAL visibility for a global
   1480 		# symbol is invalid, but for a while ld(1) was setting this
   1481 		# attribute mistakenly for protected.  If this is a dynamic
   1482 		# executable, mark its symbols as protected.  These symbols
   1483 		# can't be interposed on any more than symbols defined as
   1484 		# protected within shared objects).
   1485 		if (($Fields[5] =~ /^[IP]$/) || $Symb || $Exec) {
   1486 			$Flags |= $Prot;
   1487 		}
   1488 
   1489 		# If this object is marked as an interposer, tag each symbol.
   1490 		if ($Interpose) {
   1491 			$Flags |= $Intp;
   1492 		}
   1493 
   1494 		# Identify the symbol as a function or data type, and for the
   1495 		# latter, capture the symbol size.  Ignore the standard symbolic
   1496 		# labels, as we don't want to type them.
   1497 		if ($Fields[8] !~ $MultSyms) {
   1498 			if ($Fields[3] =~ /^FUNC$/) {
   1499 				$Flags |= $Func;
   1500 			} elsif ($Fields[3] =~ /^OBJT$/) {
   1501 				my ($Size) = $Fields[2];
   1502 
   1503 				if (oct($Size) eq 0) {
   1504 					$Size = "0";
   1505 				} else {
   1506 					$Size =~ s/0x0*/0x/;
   1507 				}
   1508 				$Flags |= $Objt;
   1509 				$Symbols{$Fields[8]}{$Obj}[$ObjSize] = $Size;
   1510 			}
   1511 		}
   1512 
   1513 		$Symbols{$Fields[8]}{$Obj}[$ObjFlag] |= $Flags;
   1514 		$Symbols{$Fields[8]}{$Obj}[$ObjVis] = $Fields[5];
   1515 		$Objects{$Obj}{$Fields[8]} |= $Flags;
   1516 	}
   1517 	close($FileHandle);
   1518 
   1519 	# Process any copy relocation symbols to see if the copy symbol has any
   1520 	# aliases, which should also be marked as copy relocations.
   1521 	if ($Copy) {
   1522 		foreach my $SymName (keys(%NameToAddr)) {
   1523 			my ($Addr) = $NameToAddr{$SymName};
   1524 
   1525 			# Determine all symbols that have the same address.
   1526 			foreach my $AliasName (@{$AddrToName{$Addr}}) {
   1527 				if ($SymName eq $AliasName) {
   1528 					next;
   1529 				}
   1530 				$Symbols{$AliasName}{$Obj}[$ObjFlag] |= $Cpyr;
   1531 				$Objects{$Obj}{$AliasName} |= $Cpyr;
   1532 			}
   1533 		}
   1534 	}
   1535 }
   1536 
   1537 # Demangle a symbol name if required.
   1538 sub Demangle
   1539 {
   1540 	my ($SymName) = @_;
   1541 	my ($DemName);
   1542 
   1543 	if ($opt{C}) {
   1544 		my (@Dem);
   1545 		
   1546 		# Determine if we've already demangled this name.
   1547 		if (exists($DemSyms{$SymName})) {
   1548 			return $DemSyms{$SymName};
   1549 		}
   1550 
   1551 		@Dem = split(/\n/, `dem '$SymName'`);
   1552 		foreach my $Line (@Dem) {
   1553 			my (@Fields) = split(' ', $Line);
   1554 
   1555 			if (($#Fields < 2) || ($Fields[1] ne '==') ||
   1556 			    ($Fields[0] eq $Fields[2])) {
   1557 				next;
   1558 			}
   1559 			$DemName = $Line;
   1560 			$DemName =~ s/.*== (.*)$/ \[$1]/;
   1561 			$DemSyms{$SymName} = $DemName;
   1562 			return($DemName);
   1563 		}
   1564 	}
   1565 	$DemSyms{$SymName} = "";
   1566 	return("");
   1567 }
   1568