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