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