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