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