1 #!/usr/perl5/bin/perl -w
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.
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.
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]
24 # Copyright 2010 Sun Microsystems, Inc. All rights reserved.
25 # Use is subject to license terms.
27 # Link Analysis of Runtime Interfaces.
30 # Define all global variables (required for strict)
31 use vars
qw($Prog $DestDir $ObjRef $ObjFlag $ObjSize $ObjVis $TmpDir);
32 use vars qw($LddArgs $SymFlag);
33 use vars qw($Glob $Intp $Dirc $Cpyr $Prot $Extn $Self $Gfte $Plta $User $Func);
34 use vars qw($Rejt $Sfte $Afte $Objt $Nodi $Osft $Oaft $Ssft $Saft $Msft);
35 use vars qw($Rtld $GlobWeak $MultSyms $CrtSyms $Platform $DbgSeed %opt);
37 # Global arrays that must be cleared for multi input file use.
38 use vars qw(%Symbols %Objects %Versioned %DemSyms %ObjFltrs %SymFltes);
45 # Pattern match to skip the runtime linker.
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
55 # Pattern matching required to determine a global symbol.
62 # Pattern matching to determine link-editor specific symbols and those common
63 # to the compilation environment (ie. provided by all crt's).
66 _GLOBAL_OFFSET_TABLE_ |
67 _PROCEDURE_LINKAGE_TABLE_ |
73 _lib_version | # Defined in values
74 __xpg4 | # Defined in values
75 __xpg6 # Defined in values
80 ___Argv | # Defined in crt
81 __environ_lock | # Defined in crt
82 _environ | # Defined in crt
83 environ # Defined in crt
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
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
111 # Offsets into $Symbols{$SymName}{$Obj} array.
117 # Offset into $SymFltr{$SymName}{$Filtee} array.
121 use POSIX qw(locale_h);
122 use Sun
::Solaris
::Utils
qw(textdomain gettext);
124 setlocale
(LC_ALL
, "");
125 textdomain
("SUNW_OST_SGS");
127 # Establish a program name for any error diagnostics.
128 $Prog = basename
($0);
131 my ($Opt1, $Opt2, $Flag) = @_;
135 gettext
("%s: inappropriate use of %s with %s: %s ignored\n"),
136 $Prog, $Opt1, $Opt2, $Opt1;
139 gettext
("%s: inappropriate use of %s without %s: %s ignored\n"),
140 $Prog, $Opt1, $Opt2, $Opt1;
144 # Cleanup any temporary files on interruption.
148 $SIG{$Sig} = 'IGNORE';
150 if ($DbgSeed ne "") {
151 foreach my $File (<\Q
${DbgSeed
}\E
.*>) {
152 if ($File =~ /^\Q$DbgSeed\E\.\d+$/) {
160 # Check that we have arguments.
161 if ((getopts
('abCDd:imosVv', \
%opt) == 0) || ($#ARGV < 0)) {
162 printf STDERR gettext
("usage:\n");
164 gettext
(" %s [-bCDsVv] [-a | -i | -o ] file | dir ...\n"), $Prog;
166 gettext
(" %s [-CDosVv] [-m [-d mapdir]] file\n"), $Prog;
168 gettext
("\t[-a] print diagnostics for all symbols\n");
170 gettext
("\t[-b] limit diagnostics to bound symbols\n");
172 gettext
("\t[-C] print demangled symbol names also\n");
174 gettext
("\t[-D] read debugging information from \"file\"\n");
176 gettext
("\t[-d dir] create mapfiles in \"mapdir\"\n");
178 gettext
("\t[-i] print interesting information (default)\n");
180 gettext
("\t[-m] create mapfiles for interface requirements\n");
182 gettext
("\t[-o] limit diagnostics to overhead information\n");
184 gettext
("\t[-s] save bindings information created by ldd(1)\n");
186 gettext
("\t[-V] append interesting symbol visibilities\n");
188 gettext
("\t[-v] ignore versioned objects\n");
193 # Catch any incompatible argument usage.
196 inappropriate
("-a", "-m", 1);
200 inappropriate
("-i", "-m", 1);
205 inappropriate
("-d", "-m", 0);
211 inappropriate
("-a", "-o", 1);
215 inappropriate
("-a", "-i", 1);
221 inappropriate
("-o", "-i", 1);
225 inappropriate
("-o", "-b", 1);
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;
237 # Insure any specified directory exists, or apply a default.
239 # User specified directory - make sure it exists.
241 printf STDERR gettext
("%s: %s is not a directory\n"),
250 # Establish a temporary directory if necessary.
252 if (!($TmpDir = $ENV{TMPDIR
}) || (! -d
$TmpDir)) {
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';
261 $LddArgs = '-r -e LD_DEBUG=bindings,files,detail';
264 # If we've been asked to demangle symbols, make sure we can find the
267 my ($DemName) = `dem XXXX 2> /dev/null`;
269 printf STDERR gettext
("%s: can not locate demangler: " .
270 "-C ignored\n"), $Prog;
275 # If -a or -o hasn't been specified, default to -i.
276 if (!$opt{a
} && !$opt{o
}) {
280 # Determine whether we have multiple input files.
287 # Determine what platform we're running on - some inappropriate
288 # platform specific dependencies are better skipped.
289 chomp($Platform = `uname -i`);
291 # Establish signal handlers
292 $SIG{INT
} = \
&Cleanup
;
293 $SIG{QUIT
} = \
&Cleanup
;
297 # For each argument determine if we're dealing with a file or directory.
299 foreach my $Arg (@ARGV) {
301 printf STDERR gettext
("%s: %s: unable to stat file\n"),
307 # Process simple files.
310 printf STDERR gettext
("%s: %s: unable to " .
311 "read file\n"), $Prog, $Arg;
316 if (ProcFile
($Arg, $Mult, 1) == 0) {
320 # If the -D option is specified, read the
321 # bindings debugging information from the
324 print STDOUT
"$Arg:\n";
326 ProcBindings
($Arg, $Mult, $Arg);
331 # Process directories.
337 printf STDERR gettext
("%s: %s: is not a file or directory\n"),
348 # Open the directory and read each entry, omit "." and "..". Sorting
349 # the directory listing makes analyzing different source hierarchies
351 if (opendir(DIR
, $Dir)) {
352 foreach my $Entry (sort(readdir(DIR
))) {
353 if (($Entry eq '.') || ($Entry eq '..')) {
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$/)) {
369 $File = "$Dir/$Entry";
378 # Descend into, and process any directories.
384 # Process any standard files.
386 ProcFile
($File, 1, 0);
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.
399 my ($File, $Mult, $CmdLine) = @_;
400 my (@Ldd, $NoFound, $DbgFile, @DbgGlob, $Type);
402 # If we're scanning a directory (ie. /lib) and have picked up ld.so.1,
404 if (($CmdLine eq 0) && ($File =~ $Rtld)) {
408 $Type = `LC_ALL=C file '$File' 2>&1`;
409 if (($Type !~ /dynamically linked/) || ($Type =~ /Sun demand paged/)) {
411 printf STDERR gettext
("%s: %s: is an invalid file " .
412 "type\n"), $Prog, $File;
417 # Create a temporary filename for capturing binding information.
418 $DbgSeed = basename
($File);
419 $DbgSeed = "$TmpDir/lari.dbg.$$.$DbgSeed";
421 # Exercise the file under ldd(1), capturing all the bindings.
423 `LC_ALL=C ldd $LddArgs -e LD_DEBUG_OUTPUT='$DbgSeed' '$File' 2>&1`);
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.
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;
435 if ($Line =~ /not found/) {
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);
451 # If there is no debug file, bail. This can occur if the file
452 # being processed is secure.
454 printf STDERR gettext
("%s: %s: unable to capture " .
455 "bindings output - possible secure application?\n"),
459 } elsif (@DbgGlob > 1) {
460 # Too many debug files found.
462 printf STDERR gettext
("%s: %s: multiple bindings " .
463 "output files exist: %s: clean up temporary " .
464 "directory\n"), $Prog, $File, $DbgSeed;
468 $DbgFile = $DbgGlob[0];
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.
477 print STDOUT
"$File:\n";
482 for my $Line (@Ldd) {
483 if ($Line =~ /not found/) {
484 print STDOUT
"$Line\n";
488 print STDOUT gettext
("\tcontinued ...\n");
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
498 rename($DbgFile, $DbgSeed);
500 printf STDOUT gettext
("%s: %s: bindings information " .
501 "saved as: %s\n"), $Prog, $File, $DbgFile;
504 ProcBindings
($File, $Mult, $DbgFile);
506 # Now that we've finished with the debugging file, nuke it if necessary.
515 my ($File, $Mult, $DbgFile) = @_;
516 my (%Filtees, $FileHandle);
518 # Reinitialize our arrays when we're dealing with multiple files.
528 # As debugging output can be significant, read a line at a time.
529 open($FileHandle, "<$DbgFile");
530 while (defined(my $Line = <$FileHandle>)) {
533 # Collect the symbols from any file analyzed.
534 if ($Line =~ /^.*: file=(.*); analyzing .*/) {
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);
548 # For greatest flexibility, split the line into fields
549 # and walk each field until we find what we need.
550 @Fields = split(' ', $Line);
552 # The referencing file, "... binding file=.* ".
553 while ($Fields[$Offset]) {
554 if ($Fields[$Offset] =~ /^file=(.*)/) {
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] =~ /^\((.*)\)/) {
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
578 while ($Fields[$Offset]) {
579 if ($Fields[$Offset] =~ /^file=(.*)/) {
587 # The symbol being bound. Over the years, we have used
588 # a ` quoting style, and more recently a ' style.
590 # "... symbol `.*' ...".
591 # "... symbol '.*' ...".
592 while ($Fields[$Offset]) {
593 if ($Fields[$Offset] =~ /^(\`|\')(.*)\'$/) {
600 # Possible trailing binding info, "... (direct,...", or
601 # a rejection, "... (rejected - ...".
602 while ($Fields[$Offset]) {
603 if ($Fields[$Offset] =~ /^\((.*)/) {
613 printf STDERR gettext
("%s: %s: debug file " .
614 "does not contain `detail' information\n"),
619 # Collect the symbols from each object.
620 GetAllSymbols
($RefFile);
621 GetAllSymbols
($DstFile);
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;
632 # This symbol is required to satisfy an external
634 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Extn;
635 $Objects{$DstFile}{$SymName} |= $Extn;
638 # Assign any other state indicated by the binding info
639 # associated with the diagnostic output.
644 if ($BndInfo =~ /direct/) {
645 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Dirc;
646 $Objects{$DstFile}{$SymName} |= $Dirc;
648 if ($BndInfo =~ /copy-ref/) {
649 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Cpyr;
650 $Objects{$DstFile}{$SymName} |= $Cpyr;
652 if ($BndInfo =~ /filtee/) {
653 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Gfte;
654 $Objects{$DstFile}{$SymName} |= $Gfte;
656 if ($BndInfo =~ /interpose/) {
657 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Intp;
658 $Objects{$DstFile}{$SymName} |= $Intp;
660 if ($BndInfo =~ /plt-addr/) {
661 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Plta;
662 $Objects{$DstFile}{$SymName} |= $Plta;
664 if ($BndInfo =~ /rejected/) {
665 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Rejt;
666 $Objects{$DstFile}{$SymName} |= $Rejt;
669 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $User;
670 $Objects{$DstFile}{$SymName} |= $User;
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)) {
685 # Determine the filtees that are associated with this filter.
686 for my $Filtee (keys(%{$ObjFltrs{$Filter}})) {
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$/) {
701 if (!exists($Objects{$Filtee})) {
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
709 for my $SymName (keys(%{$Objects{$Filtee}})) {
712 # Ignore the usual stuff.
713 if (($SymName =~ $MultSyms) ||
714 ($SymName =~ $CrtSyms)) {
718 if (!$Symbols{$SymName}{$Filter}) {
722 # Determine the type of filter.
723 $OFlag = $Symbols{$SymName}{$Filter}[$ObjFlag];
725 # Specifically identify the type of filtee we
726 # have and remove any generic filtee flag.
727 if ($OFlag & ($Osft | $Ssft)) {
733 $Symbols{$SymName}{$Filtee}[$ObjFlag] |= $FFlag;
734 $Symbols{$SymName}{$Filtee}[$ObjFlag] &= ~$Gfte;
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;
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
749 chomp($FileName = `basename $Filtee`);
750 for my $Obj (keys(%Objects)) {
751 if ($Obj =~ /\/$FileName$/) {
757 if (!exists($Objects{$FullPath})) {
761 for my $SymName (keys(%{$SymFltes{$Filtee}})) {
764 # Determine the type of filter.
765 $OFlag = $SymFltes{$Filtee}{$SymName}[$SymFlag];
767 # Specifically identify the type of filtee we have and
768 # remove any generic filtee flag.
769 if ($OFlag & $Ssft) {
775 $Symbols{$SymName}{$FullPath}[$ObjFlag] |= $FFlag;
776 $Symbols{$SymName}{$FullPath}[$ObjFlag] &= ~$Gfte;
780 # Process objects and their symbols as required.
782 # If we're creating a mapfile, traverse each object we've
784 foreach my $Obj (keys(%Objects)) {
787 # Skip any objects that should be ignored.
792 # Skip any versioned objects if required.
793 if ($opt{v
} && $Versioned{$Obj}) {
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, $!;
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";
811 # Process each global symbol.
812 print MAPOUT
"$File {\n\tglobal:\n";
814 foreach my $SymName (sort(keys(%{$Objects{$Obj}}))) {
815 my ($Flag) = $Objects{$Obj}{$SymName};
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))) {
826 if (($Flag & ($Extn | $Self)) == $Self) {
830 # Add the demangled name as a comment if
833 my ($DemName) = Demangle
($SymName);
835 if ($DemName ne "") {
836 print MAPOUT
"\t\t#$DemName\n";
839 print MAPOUT
"\t\t$SymName;\n";
842 # Process each local demotion.
843 print MAPOUT
"\tlocal:\n";
847 (sort(keys(%{$Objects{$Obj}}))) {
848 my ($Flag) = $Objects{$Obj}{$SymName};
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.
858 # Add the demangled name as a comment if
864 if ($DemName ne "") {
869 print MAPOUT
"\t\t$SymName;\n";
873 # Capture everything else as local.
874 print MAPOUT
"\t\t\*;\n};\n";
879 # If we're gathering information regarding the symbols used by
880 # the process, automatically sort any standard output using the
882 if (!open(SORT
, "| sort +1")) {
883 printf STDERR gettext
("%s: fork failed: %s\n"),
888 foreach my $SymName (keys(%Symbols)) {
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) {
898 # We've found something interesting, or all symbols
899 # should be output. List all objects that define this
901 foreach my $Obj (keys(%{$Symbols{$SymName}})) {
902 my ($DemName, $Type);
903 my ($Flag) = $Symbols{$SymName}{$Obj}[$ObjFlag];
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)) {
916 # Do we just want all symbols that have been
918 if (($opt{a
} || $opt{o
}) && $opt{b
} &&
919 (($Flag & ($Extn | $Self | $Prot)) == 0)) {
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)))) {
939 # Skip any versioned objects if required.
940 if ($opt{v
} && $Versioned{$Obj}) {
944 # Display this symbol.
945 if ($Symbols{$SymName}{$Obj}[$ObjRef]) {
947 $Symbols{$SymName}{$Obj}[$ObjRef];
952 # Has the symbol been bound to externally
956 # Has the symbol been bound to from the same
961 # Has the symbol been bound to directly.
965 # Does this symbol originate for an explicit
970 # Is this symbol the reference data of a copy
975 # Is this symbol part of filtee.
976 if ($Flag & ($Sfte | $Afte | $Gfte)) {
979 # Is this symbol protected (in which case there
980 # may be a symbolic binding within the same
981 # object to this symbol).
985 # Is this symbol an executables .plt address.
989 # Does this binding originate from a user
994 # Does this definition redirect the binding.
998 # Does this definition explicitly define no
1000 if ($Flag & $Nodi) {
1003 # Was a binding to this definition rejected at
1005 if ($Flag & $Rejt) {
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) {
1015 } elsif ($Flag & $Objt) {
1017 $Symbols{$SymName}{$Obj}[$ObjSize] .
1023 # Demangle the symbol name if desired.
1024 $DemName = Demangle
($SymName);
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) {
1032 $DisVis = " (singleton)";
1033 } elsif ($Vis =~ 'P') {
1034 $DisVis = " (protected)";
1038 print SORT
" [$Str]: " .
1039 "$SymName$Type$DemName: " .
1042 print SORT
"[$Str]: " .
1043 "$SymName$Type$DemName: " .
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.
1058 # Note, this routine is actually called for all symbols so that their count
1059 # can be calculated in one place.
1063 my ($ObjCnt, $GFlags, $BndCnt, $FltCnt, $NodiCnt, $RdirCnt, $ExRef);
1064 my ($RejCnt, $TotCnt);
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];
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) {
1083 # If we're only looking at interesting objects, then standard
1084 # filters are ignored, so suppress any standard filtee tagging.
1086 $Flag = $Symbols{$SymName}{$Obj}[$ObjFlag] &= ~$Sfte;
1090 if ($Flag & ($Sfte | $Afte | $Gfte)) {
1093 if ($Flag & $Nodi) {
1096 if ($Flag & ($Cpyr | $Plta)) {
1099 if ($Flag & $Msft) {
1102 if ($Flag & $Rejt) {
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)) {
1117 # If we want all overhead symbols, return the count.
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))) {
1128 # Any rejected symbol is interesting
1133 # Single instance symbol definitions aren't very interesting.
1138 # Traverse each symbol definition looking for the following:
1140 # . Multiple symbols are bound to externally.
1141 # . A symbol is bound to externally, and possibly symbolically.
1143 # Two symbol bindings are acceptable in some cases, and thus aren't
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
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
1155 # Multiple symbol bindings are acceptable in some cases, and thus aren't
1158 # . Filtees. Multiple filtees may exist for one filter.
1160 if ((($ObjCnt == 2) && ($GFlags & ($Cpyr | $Plta))) ||
1161 ($ObjCnt == ($FltCnt + 1))) {
1165 # Only display any reserved symbols if more than one binding has
1167 if ((($SymName =~ $MultSyms) || ($SymName =~ $CrtSyms)) &&
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
1176 if ($opt{b
} && ($BndCnt == 0)) {
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)) {
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) {
1195 # We have an interesting symbol, returns its count.
1199 # Obtain the global symbol definitions of an object and determine whether the
1200 # object has been versioned.
1203 my ($Type, $FileHandle);
1204 my (%AddrToName, %NameToAddr);
1208 my ($Interpose) = 0;
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}) {
1224 if ($Obj =~ $Rtld) {
1225 $Versioned{$Obj} = 1;
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.
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
1241 # As this information can be quite large, process the elfdump(1) output
1243 open($FileHandle, "LC_ALL=C elfdump -edrvy '$Obj' 2> /dev/null |");
1245 while (defined(my $Line = <$FileHandle>)) {
1250 # Each collection of data is preceded with a title that
1251 # starts in column 0. Items of data all have some form of
1253 if ($Line =~ /^[A-Z]/) {
1254 if ($Line =~ /^ELF Header/) {
1256 $Dyn = $Rel = $Info = 0;
1257 } elsif ($Line =~ /^Dynamic Section:/) {
1259 $Ehdr = $Rel = $Info = 0;
1260 } elsif ($Line =~ /^Relocation Section:/) {
1262 $Ehdr = $Dyn = $Info = 0;
1263 } elsif ($Line =~ /^Syminfo Section:/) {
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;
1273 $Ehdr = $Dyn = $Rel = $Info = 0;
1278 # Inspect the ELF header.
1280 # Determine the ELF file type from the e_type element.
1281 if ($Line =~ /e_type:/) {
1282 if ($Line =~ /ET_EXEC/) {
1286 # There's nothing of interest left in the ELF
1287 # header, so skip processing other entries.
1293 # Inspect the .dynamic section.
1295 @Fields = split(' ', $Line);
1297 # Determine if the FILTER or AUXILIARY tag is set.
1298 if ($#Fields == 3) {
1301 if ($Fields[1] eq 'FILTER') {
1305 elsif ($Fields[1] eq 'AUXILIARY') {
1310 my (@Filtees) = split(':', $Fields[3]);
1312 for my $Filtee (@Filtees) {
1313 if ($Filtee =~ $Rtld) {
1316 $ObjFltrs{$Obj}{$Filtee} = 1;
1322 # We're only interested in the FLAGS entry.
1323 if (($#Fields < 4) || ($Fields[1] !~ /^FLAGS/)) {
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
1332 if (($Fields[1] eq 'FLAGS') &&
1333 ($Line =~ / SYMBOLIC /)) {
1338 # Determine whether this object is an interposer.
1339 if (($Fields[1] eq 'FLAGS_1') &&
1340 ($Line =~ / OBJECT-INTERPOSE /)) {
1347 # Inspect the relocation information. As we're only looking
1348 # for copy relocations, this processing is only necessary for
1358 # Obtain any copy relocations.
1359 if ($Line !~ / R_[A-Z0-9]+_COPY /) {
1363 @Fields = split(' ', $Line);
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];
1370 $SymName = $Fields[4];
1373 $Symbols{$SymName}{$Obj}[$ObjFlag] |= $Cpyr;
1374 $Objects{$Obj}{$SymName} |= $Cpyr;
1378 # Inspect the .SUNW_syminfo section.
1383 @Fields = split(' ', $Line);
1385 # Binding attributes are in the second column.
1389 if ($Fields[1] =~ /N/) {
1392 if ($Fields[1] =~ /F/) {
1395 if ($Fields[1] =~ /A/) {
1398 if ($Fields[1] =~ /I/) {
1402 # Determine the symbol name based upon the number of
1405 $SymName = $Fields[$#Fields];
1406 $Symbols{$SymName}{$Obj}[$ObjFlag] |= $Flags;
1407 $Objects{$Obj}{$SymName} |= $Flags;
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);
1415 my ($Filtee) = $Fields[$#Fields - 1];
1417 if ($Filtee =~ $Rtld) {
1420 $SymFltes{$Filtee}{$SymName}[$SymFlag] = $Flags;
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.
1431 printf STDERR gettext
("%s: %s: unable to process ELF file\n"),
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;
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 |");
1445 while (defined(my $Line = <$FileHandle>)) {
1448 my (@Fields) = split(' ', $Line);
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')))) {
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]);
1472 if (($Symbols{$Fields[8]}{$Obj}) &&
1473 ($Symbols{$Fields[8]}{$Obj}[$ObjFlag] & $Cpyr)) {
1474 $NameToAddr{$Fields[8]} = $Fields[1];
1478 # Identify this symbol as global, and associate it with any
1480 $Flags = $Glob | $Fltr;
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) {
1493 # If this object is marked as an interposer, tag each symbol.
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$/) {
1504 } elsif ($Fields[3] =~ /^OBJT$/) {
1505 my ($Size) = $Fields[2];
1507 if (oct($Size) eq 0) {
1510 $Size =~ s/0x0*/0x/;
1513 $Symbols{$Fields[8]}{$Obj}[$ObjSize] = $Size;
1517 $Symbols{$Fields[8]}{$Obj}[$ObjFlag] |= $Flags;
1518 $Symbols{$Fields[8]}{$Obj}[$ObjVis] = $Fields[5];
1519 $Objects{$Obj}{$Fields[8]} |= $Flags;
1523 # Process any copy relocation symbols to see if the copy symbol has any
1524 # aliases, which should also be marked as copy relocations.
1526 foreach my $SymName (keys(%NameToAddr)) {
1527 my ($Addr) = $NameToAddr{$SymName};
1529 # Determine all symbols that have the same address.
1530 foreach my $AliasName (@
{$AddrToName{$Addr}}) {
1531 if ($SymName eq $AliasName) {
1534 $Symbols{$AliasName}{$Obj}[$ObjFlag] |= $Cpyr;
1535 $Objects{$Obj}{$AliasName} |= $Cpyr;
1541 # Demangle a symbol name if required.
1550 # Determine if we've already demangled this name.
1551 if (exists($DemSyms{$SymName})) {
1552 return $DemSyms{$SymName};
1555 @Dem = split(/\n/, `dem '$SymName'`);
1556 foreach my $Line (@Dem) {
1557 my (@Fields) = split(' ', $Line);
1559 if (($#Fields < 2) || ($Fields[1] ne '==') ||
1560 ($Fields[0] eq $Fields[2])) {
1564 $DemName =~ s/.*== (.*)$/ \[$1]/;
1565 $DemSyms{$SymName} = $DemName;
1569 $DemSyms{$SymName} = "";