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