Hallo, dies ist ein Test.
PWD: /www/data-lst1/unixsoft/unixsoft/kaempfer/.public_html
Running in File Mode
Relative path: ./../.././.././../../../bin/lari
Real path: /usr/bin/lari
Zurück
#!/usr/perl5/5.36/bin/perl # # Copyright (c) 2003, 2021, Oracle and/or its affiliates. # # Link Analysis of Runtime Interfaces. # # Define all global variables (required for strict) use vars qw($Prog $DestDir $TmpDir); use vars qw($LddArgs $KlddArgs); use vars qw($Rtld $GlobWeak $MultSyms $CrtSyms $KrtldSyms $Platform $DbgSeed); use vars qw(%opt $DemPath); # Global arrays that must be cleared for multi input file use. use vars qw(%Symbols %Objects %Versioned %DemSyms %ObjFltrs %SymFltes); use strict; use Getopt::Std; use File::Basename; use Config; # Pattern match to skip the runtime linker. $Rtld = qr{ /lib/ld\.so\.1 | /usr/lib/ld\.so\.1 | /lib/sparcv9/ld\.so\.1 | /usr/lib/sparcv9/ld\.so\.1 | /lib/amd64/ld\.so\.1 | /usr/lib/amd64/ld\.so\.1 }x; # Pattern matching required to determine a global symbol. $GlobWeak = qr{ ^(?: GLOB | WEAK )$ }x; # Pattern matching to determine link-editor specific symbols and those common # to the compilation environment (ie. provided by all crt's). $MultSyms = qr{ ^(?: _DYNAMIC | _GLOBAL_OFFSET_TABLE_ | _PROCEDURE_LINKAGE_TABLE_ | _etext | _edata | _end | _init | _fini | _lib_version | # Defined in values __xpg4 | # Defined in values __xpg6 # Defined in values )$ }x; $CrtSyms = qr{ ^(?: ___Argv | # Defined in crt __environ_lock | # Defined in crt _environ | # Defined in crt environ # Defined in crt )$ }x; # Kernel Module Symbols that are accessed by krtld and should not be # reduced to local scope. $KrtldSyms = qr{ ^(?: _fini | _info | _init )$ }x; # Symbol flags. Used to retain information about symbols needed for analysis. use constant { S_F_GLOB => 0x000001, # global S_F_SFTE => 0x000002, # filtee backing a standard filter S_F_AFTE => 0x000004, # filtee backing a auxiliary filter S_F_GFTE => 0x000008, # symbol bound as a filtee S_F_INTP => 0x000010, # originates from explicit interposer S_F_DIRC => 0x000020, # symbol bound to directly S_F_CPYR => 0x000040, # bound to copy-relocation reference S_F_PROT => 0x000080, # protected (symbolic) S_F_EXTN => 0x000100, # bound to from an external reference S_F_SELF => 0x000200, # bound to from the same object S_F_REJT => 0x000400, # binding was (at some point) rejected S_F_PLTA => 0x000800, # bound to executable's plt address S_F_USER => 0x001000, # binding originates from user (dlsym) S_F_FUNC => 0x002000, # function S_F_OBJT => 0x004000, # object (data symbol) S_F_NODI => 0x008000, # symbol prohibits direct binding S_M_FILT => 0x0f0000, # filter mask S_F_OSFT => 0x010000, # symbol is an standard object filter S_F_OAFT => 0x020000, # symbol is an auxiliary object filter S_F_SSFT => 0x040000, # per-symbol standard filter S_F_SAFT => 0x080000, # per-symbol auxiliary filter S_F_MODSTUB => 0x100000, # kernel modstub S_F_KRTLD => 0x200000 # special krtld symbols ($KrtldSyms) }; # Indexes into $Symbols{$SymName}{$Obj} array. use constant { IX_OBJ_REF => 0, IX_OBJ_FLAG => 1, IX_OBJ_SIZE => 2, IX_OBJ_VIS => 3 }; # Index into $SymFltes{$SymName}{$Filtee} array. use constant { IX_SFTE_FLAG => 0 }; # Identify the source of the data being analyzed. Userland objects are # examined using ldd (with LD_DEBUG enabled), while kernel modules are # examined with kldd. use constant { LARI_T_UNKNOWN => 0, LARI_T_USERLAND => 1, LARI_T_KERNEL => 2 }; # Establish locale use POSIX qw(locale_h); use Sun::Solaris::Utils qw(textdomain gettext); setlocale(LC_ALL, ""); textdomain("solaris_linkers"); # Establish a program name for any error diagnostics. $Prog = basename($0); sub inappropriate { my ($Opt1, $Opt2, $Flag) = @_; if ($Flag) { printf STDERR gettext("%s: inappropriate use of %s with %s: %s ignored\n"), $Prog, $Opt1, $Opt2, $Opt1; } else { printf STDERR gettext("%s: inappropriate use of %s without %s: %s ignored\n"), $Prog, $Opt1, $Opt2, $Opt1; } } # Cleanup any temporary files on interruption. sub Cleanup { my ($Sig) = @_; $SIG{$Sig} = 'IGNORE'; if ($DbgSeed ne "") { foreach my $File (<\Q${DbgSeed}\E.*>) { if ($File =~ /^\Q$DbgSeed\E\.\d+$/) { unlink($File); } } } exit 1; } # Send usage message to given file handle. # sub Usage { my $fh = $_[0]; print $fh gettext("usage:\n"); printf $fh gettext(" %s [-bCDsvV] [-a | -i | -o ] file | dir ...\n"), $Prog; printf $fh gettext(" %s [-CDosvV] [-m [-d mapdir]] file\n"), $Prog; print $fh "\n"; print $fh gettext("\t-a\t\tprint diagnostics for all symbols\n"); print $fh gettext("\t-b\t\tlimit diagnostics to bound symbols\n"); print $fh gettext("\t-C\t\tprint demangled symbol names also\n"); print $fh gettext("\t-d dir\t\tcreate mapfiles in \"mapdir\"\n"); print $fh gettext("\t-D\t\tread debugging information from \"file\"\n"); print $fh gettext("\t-i\t\tprint interesting information (default)\n"); print $fh gettext("\t-m\t\tcreate mapfiles for interface requirements\n"); print $fh gettext("\t-o\t\tlimit diagnostics to overhead information\n"); print $fh gettext("\t-s\t\tsave bindings information created by ldd(1)\n"); print $fh gettext("\t-v\t\tignore versioned objects\n"); print $fh gettext("\t-V\t\tappend interesting symbol visibilities\n"); print $fh gettext("\t--version\tprint version information\n"); print $fh gettext("\t-?, --help\tprint this usage message\n"); } # The Solaris standard is for -?/--help to send usage information to # stdout, and then to exit immediately. Similarly, -V/--version send # version information to stdout and exit immediately. # # -V is already taken by lari, and so, is not available. This loop # over ARGV handles the others. Note that getopts() can support # --help and --version via their HELP_MESSAGE and VERSION_MESSAGE # mechanism, but not -?. foreach my $arg (@ARGV) { if (($arg eq '-?') || ($arg eq '--help')) { Usage(*STDOUT); exit 0; } if ($arg eq '--version') { # lari is delivered in sync with the rest of the ELF # components, and so, will have the same version number # as the others. Use mcs to obtain the version embedded # in the C utilities, and alter the utility name. my $version = `/usr/bin/mcs -V 2>&1`; $version =~ s/^[^:]*:/$Prog:/; print $version; exit 0; } } # Check that we have arguments. if ((getopts('abCDd:imosVv', \%opt) == 0) || ($#ARGV < 0)) { Usage(*STDERR); exit 1; } else { my ($Mult, $Error); # Catch any incompatible argument usage. if ($opt{m}) { if ($opt{a}) { inappropriate("-a", "-m", 1); $opt{a} = 0; } if ($opt{i}) { inappropriate("-i", "-m", 1); $opt{i} = 0; } } else { if ($opt{d}) { inappropriate("-d", "-m", 0); $opt{d} = 0; } } if ($opt{a}) { if ($opt{o}) { inappropriate("-a", "-o", 1); $opt{o} = 0; } if ($opt{i}) { inappropriate("-a", "-i", 1); $opt{i} = 0; } } if ($opt{o}) { if ($opt{i}) { inappropriate("-o", "-i", 1); $opt{i} = 0; } if ($opt{b}) { inappropriate("-o", "-b", 1); $opt{b} = 0; } } # If -m is used, only one input file is applicable. if ($opt{m} && ($#ARGV != 0)) { printf STDERR gettext("%s: only one input file is allowed " . "with the -m option\n"), $Prog; exit 1; } # Insure any specified directory exists, or apply a default. if ($opt{d}) { # User specified directory - make sure it exists. if (! -d $opt{d}) { printf STDERR gettext("%s: %s is not a directory\n"), $Prog, $opt{d}; exit 1; } $DestDir = $opt{d}; } else { $DestDir = "."; } # Establish a temporary directory if necessary. if (!$opt{D}) { if (!($TmpDir = $ENV{TMPDIR}) || (! -d $TmpDir)) { $TmpDir = "/tmp"; } } # Establish any initial ldd(1) and kldd(1) argument requirements. if ($LddArgs = $ENV{LARI_LDD_ARGS}) { $LddArgs = $LddArgs . ' -r -e LD_DEBUG=bindings,files,detail'; } else { $LddArgs = '-r -e LD_DEBUG=bindings,files,detail'; } if ($KlddArgs = $ENV{LARI_KLDD_ARGS}) { $KlddArgs = $KlddArgs . ' -rb'; } else { $KlddArgs = '-rb'; } # If we've been asked to demangle symbols, find the fully qualified # path to the Solaris Studio 'dem' demangler. The compilers can be # installed at a user selected location, so we must do a PATH search. # dem is called for each symbol, so the use of a fully qualified # path can save a large amount of per-invocation path searching. $DemPath = ''; if ($opt{C}) { foreach my $Path (split /:/,$ENV{PATH}) { $DemPath = "$Path/dem"; if (-x $DemPath) { my $DemName = `$DemPath XXXX 2> /dev/null`; $DemPath = '' if !$DemName; last; } $DemPath = ''; } if ($DemPath eq '') { printf STDERR gettext("%s: cannot locate demangler: " . "-C ignored\n"), $Prog; $opt{C} = 0; } } # If -a or -o hasn't been specified, default to -i. if (!$opt{a} && !$opt{o}) { $opt{i} = 1; } # Determine whether we have multiple input files. if ($#ARGV == 0) { $Mult = 0; } else { $Mult = 1; } # Determine what platform we're running on - some inappropriate # platform specific dependencies are better skipped. chomp($Platform = `/usr/bin/uname -i`); # Establish signal handlers $SIG{INT} = \&Cleanup; $SIG{QUIT} = \&Cleanup; $DbgSeed = ""; # For each argument determine if we're dealing with a file or directory. $Error = 0; foreach my $Arg (@ARGV) { if (!stat($Arg)) { printf STDERR gettext("%s: %s: unable to stat file\n"), $Prog, $Arg; $Error = 1; next; } # Process simple files. if (-f _) { if (!-r _) { printf STDERR gettext("%s: %s: unable to " . "read file\n"), $Prog, $Arg; $Error = 1; next; } if (!$opt{D}) { if (ProcFile($Arg, $Mult, 1) == 0) { $Error = 1; } } else { # If the -D option is specified, read the # bindings debugging information from the # specified file. if ($Mult) { print STDOUT "$Arg:\n"; } ProcBindings(LARI_T_UNKNOWN, $Arg, $Mult, $Arg); } next; } # Process directories. if (-d _) { ProcDir($Arg); next; } printf STDERR gettext("%s: %s: is not a file or directory\n"), $Prog, $Arg; $Error = 1; } exit $Error; } sub ProcDir { my ($Dir) = @_; my ($File); # Open the directory and read each entry, omit "." and "..". Sorting # the directory listing makes analyzing different source hierarchies # easier. if (opendir(DIR, $Dir)) { foreach my $Entry (sort(readdir(DIR))) { if (($Entry eq '.') || ($Entry eq '..')) { next; } # If we're descending into a platform directory, ignore # any inappropriate platform specific files. These # files can have dependencies that in turn bring in the # appropriate platform specific file, resulting in more # than one dependency offering the same interfaces. In # practice, the non-appropriate platform specific file # wouldn't be loaded with a process. if (($Dir =~ /\/platform$/) && ($Entry !~ /^$Platform$/)) { next; } $File = "$Dir/$Entry"; if (!lstat($File)) { next; } # Ignore symlinks. if (-l _) { next; } # Descend into, and process any directories. if (-d _) { ProcDir($File); next; } # Process any standard files. if (-f _ && -r _) { ProcFile($File, 1, 0); next; } } closedir(DIR); } } ## ProcFile(File, Mult, CmdLine) # # Process a file. If the file was explicitly defined on the command-line, and # an error occurs, tell the user. Otherwise, this file probably came about from # scanning a directory, in which case just skip it and move on. # sub ProcFile { my ($File, $Mult, $CmdLine) = @_; my (@Ldd, $NoFound, $DbgFile, @DbgGlob, $LariType, $Cmd); # If we're scanning a directory (ie. /lib) and have picked up ld.so.1, # ignore it. if (($CmdLine eq 0) && ($File =~ $Rtld)) { return 1; } # Determine the type of object being examined TYPE: { my $str = `LC_ALL=C /usr/bin/file '$File' 2>&1`; if ($str =~ /kernel module/) { $LariType = LARI_T_KERNEL; last TYPE; } if (($str =~ /dynamically linked/) && ($str !~ /Sun demand paged/)) { $LariType = LARI_T_USERLAND; last TYPE; } if ($CmdLine) { printf STDERR gettext("%s: %s: is an invalid file type\n"), $Prog, $File; } return 0; } # Create a temporary filename for capturing binding information. $DbgSeed = basename($File); $DbgSeed = "$TmpDir/lari.dbg.$$.$DbgSeed"; $NoFound = 0; if ($LariType == LARI_T_KERNEL) { # Kernel Module # Exercise the file under kldd(1), capturing all the bindings. my ($InFileHandle, $OutFileHandle); # kldd does not add a pid suffix to the output file, so # manually add one. This allows the code for the -s option, # and file cleanup, to be identical in the kldd/ldd cases. $DbgFile = "$DbgSeed.$$"; if (!open($OutFileHandle, ">$DbgFile")) { printf STDERR gettext("%s: %s: open failed:%s\n"), $Prog, $DbgFile, $!; exit 1; } open($InFileHandle, "LC_ALL=C /usr/bin/kldd $KlddArgs '$File' 2>&1|"); # kldd does not list the primary object as a dependency. # Add a fake dependency, so that ProcBindings() can see it # and arrange for GetAllSymbols() to be called for it. print $OutFileHandle "\tlari (command line) =>\t$File\n"; # Transfer all kldd output to the debug file, while capturing # non-binding output in @Ldd for the "NoFound" check below. @Ldd = (); while (defined(my $Line = <$InFileHandle>)) { printf $OutFileHandle $Line; chomp $Line; if ($Line =~ /not found/) { $NoFound = 1; push(@Ldd, $Line); } elsif ($Line !~ /^binding(\s+\(.*\))?:\s+/) { push(@Ldd, $Line); } } close $InFileHandle; close $OutFileHandle; } else { # Userland object # Exercise the file under ldd(1), capturing all the bindings. $Cmd = "LC_ALL=C /usr/bin/ldd $LddArgs " . "-e LD_DEBUG_OUTPUT='$DbgSeed' '$File' 2>&1"; @Ldd = split(/\n/, `$Cmd`); # Make sure the user sees any ldd errors. for my $Line (@Ldd) { # The unix kernel is an executable, but not a userland # executable. ldd will fail to load it with an error # about the ELF header not being mapped. Use that # error to detect an attempt to analyze the kernel # and supply a straightforward error for that case. if ($Line =~ /file built with \?N \(NOHDR\)/) { printf STDERR gettext("%s: %s: unable to " . "analyze kernel object\n"), $Prog, $File; return 0; } if ($Line =~ /not found/) { $NoFound = 1; last; } } } # The runtime linker will have appended a process id to the debug file. # As we have to intuit the name, make sure there is only one debug # file match, otherwise there must be some clutter in the output # directory that is going to mess up our analysis. foreach my $Match (<\Q${DbgSeed}\E.*>) { if ($Match =~ /^\Q$DbgSeed\E\.\d+$/) { push(@DbgGlob, $Match); } } if (@DbgGlob == 0) { # If there is no debug file, bail. This can occur if the file # being processed is secure. if ($CmdLine) { printf STDERR gettext("%s: %s: unable to capture " . "bindings output - possible secure application?\n"), $Prog, $File; } return 0; } elsif (@DbgGlob > 1) { # Too many debug files found. if ($CmdLine) { printf STDERR gettext("%s: %s: multiple bindings " . "output files exist: %s: clean up temporary " . "directory\n"), $Prog, $File, $DbgSeed; } return 0; } else { $DbgFile = $DbgGlob[0]; } # Ok, we're ready to process the bindings information. Print a header # if necessary, and if there were any ldd(1) errors push some of them # out before any bindings information. Limit the output, as it can # sometimes be excessive. If there are errors, the bindings information # is likely to be incomplete. if ($Mult) { print STDOUT "$File:\n"; } if ($NoFound) { my ($Cnt) = 4; for my $Line (@Ldd) { if ($Line =~ /not found/) { print STDOUT "$Line\n"; $Cnt--; } if ($Cnt == 0) { print STDOUT gettext("\tcontinued ...\n"); last; } } } # If the user wants the original debugging file left behind, rename it # so that it doesn't get re-read by another instance of lari processing # this file. if ($opt{s}) { rename($DbgFile, $DbgSeed); $DbgFile = $DbgSeed; printf STDOUT gettext("%s: %s: bindings information " . "saved as: %s\n"), $Prog, $File, $DbgFile; } ProcBindings($LariType, $File, $Mult, $DbgFile); # Now that we've finished with the debugging file, nuke it if necessary. if (!$opt{s}) { unlink($DbgFile); } $DbgSeed = ""; return 1; } # ProcBindingsUserland(DbgFile) # # Process the data produced for userland objects using ldd/LD_DEBUG. # sub ProcBindingsUserland { my ($DbgFile) = @_; my ($FileHandle); # As debugging output can be significant, read a line at a time. open($FileHandle, "<$DbgFile"); while (defined(my $Line = <$FileHandle>)) { chomp($Line); # Collect the symbols from any file analyzed. if ($Line =~ /^.*: file=(.*); analyzing .*/) { GetAllSymbols($1); next; } # Process any symbolic relocations that bind to a file. if ($Line =~ /: binding file=.* to file=/) { my ($RefFile, $DstFile, $SymName); my (@Syms, $Found, @Fields); my ($BndInfo) = 0; my ($Offset) = 1; my ($Dlsym) = 0; my ($Detail) = 0; # For greatest flexibility, split the line into fields # and walk each field until we find what we need. @Fields = split(' ', $Line); # The referencing file, "... binding file=.* ". while ($Fields[$Offset]) { if ($Fields[$Offset] =~ /^file=(.*)/) { $RefFile = $1; $Offset++; last; } $Offset++; } # The referencing offset, typically this is the address # of the reference, "(0x1234...)", but in the case of a # user lookup it is the string "(dlsym)". If we don't # find this offset information we've been given a debug # file that didn't use the "detail" token, in which case # we're not getting all the information we need. if ($Fields[$Offset] =~ /^\((.*)\)/) { if ($1 eq 'dlsym') { $Dlsym = 1; } $Detail = 1; $Offset++; } # The destination file, "... to file=.* ". Note, in the # case of a rejection message, the file is terminated # with a colon, "... to file=.*: ", which must be # removed while ($Fields[$Offset]) { if ($Fields[$Offset] =~ /^file=(.*)/) { $DstFile = $1; $DstFile =~ s/:$//; $Offset++; last; } $Offset++; } # The symbol being bound. Over the years, we have used # a ` quoting style, and more recently a ' style. # Match either of: # "... symbol `.*' ...". # "... symbol '.*' ...". while ($Fields[$Offset]) { if ($Fields[$Offset] =~ /^(\`|\')(.*)\'$/) { $SymName = $2; $Offset++; last; } $Offset++; } # Possible trailing binding info, "... (direct,...", or # a rejection, "... (rejected - ...". while ($Fields[$Offset]) { if ($Fields[$Offset] =~ /^\((.*)/) { $BndInfo = $1; $Detail = 1; $Offset++; last; } $Offset++; } if ($Detail == 0) { printf STDERR gettext("%s: %s: debug file " . "does not contain `detail' information\n"), $Prog, $DbgFile; return 0; } # Collect the symbols from each object. GetAllSymbols($RefFile); GetAllSymbols($DstFile); # Identify that this definition has been bound to. $Symbols{$SymName}{$DstFile}[IX_OBJ_REF]++; if ($RefFile eq $DstFile) { # If the reference binds to a definition within # the same file this symbol may be a candidate # for reducing to local. $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_SELF; $Objects{$DstFile}{$SymName} |= S_F_SELF; } else { # This symbol is required to satisfy an external # reference. $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_EXTN; $Objects{$DstFile}{$SymName} |= S_F_EXTN; } # Assign any other state indicated by the binding info # associated with the diagnostic output. if (!$BndInfo) { next; } if ($BndInfo =~ /direct/) { $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_DIRC; $Objects{$DstFile}{$SymName} |= S_F_DIRC; } if ($BndInfo =~ /copy-ref/) { $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_CPYR; $Objects{$DstFile}{$SymName} |= S_F_CPYR; } if ($BndInfo =~ /filtee/) { $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_GFTE; $Objects{$DstFile}{$SymName} |= S_F_GFTE; } if ($BndInfo =~ /interpose/) { $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_INTP; $Objects{$DstFile}{$SymName} |= S_F_INTP; } if ($BndInfo =~ /plt-addr/) { $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_PLTA; $Objects{$DstFile}{$SymName} |= S_F_PLTA; } if ($BndInfo =~ /rejected/) { $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_REJT; $Objects{$DstFile}{$SymName} |= S_F_REJT; } if ($Dlsym) { $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_USER; $Objects{$DstFile}{$SymName} |= S_F_USER; } } } close($FileHandle); return 1; } # ProcBindingsKernel(DbgFile) # # Process the data produced for kernel modules using kldd. # sub ProcBindingsKernel { my ($DbgFile) = @_; my $FileHandle; my ($RefFile, $DstFile, $SymName); my $CurRefFile = my $CurDstFile = ''; # As debugging output can be significant, read a line at a time. open($FileHandle, "<$DbgFile"); while (defined(my $Line = <$FileHandle>)) { chomp($Line); # Get symbols from objects identified by lines of the form: # XXX => YYY if ($Line =~ /^.*=>\s+([^\s]+)\s*$/) { GetAllSymbols($1); next; } # Lines of of the following form are of interest. Note that # there can be an optional annotation after "binding" that # reports attributes (e.g. modstub): # binding: symbol (ref=file: def=file) # binding (...): symbol (ref=file: def=file) next if ($Line !~ /^binding(\s+\(.*\))?:\s+([^\s]+)\s+\(ref=([^\s]+):\s+def=([^\s]+)\)$/); $SymName = $2; $RefFile = $3; $DstFile = $4; if ($CurRefFile ne $RefFile) { $CurRefFile = $RefFile; GetAllSymbols($RefFile); } if ($CurDstFile ne $DstFile) { $CurDstFile = $DstFile; GetAllSymbols($DstFile); } # Identify that this definition has been bound to. $Symbols{$SymName}{$DstFile}[IX_OBJ_REF]++; if ($RefFile eq $DstFile) { # If the reference binds to a definition within # the same file this symbol may be a candidate # for reducing to local. $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_SELF; $Objects{$DstFile}{$SymName} |= S_F_SELF; } else { # This symbol is required to satisfy an external # reference. $Symbols{$SymName}{$DstFile}[IX_OBJ_FLAG] |= S_F_EXTN; $Objects{$DstFile}{$SymName} |= S_F_EXTN; } } close($FileHandle); return 1; } ## ProcBindings(LariType, File, Mult, DbgFile) # # Process bindings information from the output from ldd/kldd. # # entry: # LariType - LARI_T_* value indicating source of output to be processed. # sub ProcBindings { my ($LariType, $File, $Mult, $DbgFile) = @_; my (%Filtees); # Reinitialize our arrays when we're dealing with multiple files. if ($Mult) { %Symbols = (); %Objects = (); %Versioned = (); %DemSyms = (); %ObjFltrs = (); %SymFltes = (); } # If the type of DbgFile is unknown, read the first few lines # to figure it out. This relies on the initial lines of userland # debug files produced by ld.so.1 being of the form: # # debug: # debug: Solaris ELF Utilities: xxx # debug: # if ($LariType == LARI_T_UNKNOWN) { my $FileHandle; $LariType = LARI_T_KERNEL; open($FileHandle, "head -3 $DbgFile|"); while (defined(my $Line = <$FileHandle>)) { chomp($Line); if ($Line =~ /^debug: Solaris ELF Utilities:/) { $LariType = LARI_T_USERLAND; last; } } close ($FileHandle); } if ($LariType == LARI_T_KERNEL) { ProcBindingsKernel($DbgFile) || return; } else { ProcBindingsUserland($DbgFile) || return; } # Now that we've processed all objects, traverse the set of object # filters that have been captured from parsing any FILTER and AUXILIARY # dynamic tags. For each filtee, determine which of the symbols it # exports are also defined in the filter. If a filter is bound to, the # runtime linkers diagnostics will indicate a filtee binding. However, # some of the filtee symbols may not be bound to, so here we mark them # all so as to remove them from any interesting output. for my $Filter (keys(%ObjFltrs)) { # Determine the filtees that are associated with this filter. for my $Filtee (keys(%{$ObjFltrs{$Filter}})) { my ($FileName); # Reduce the filtee to a simple file name. Then, try # and associate this simple file name with the objects # that have been processed. These objects are typically # recorded with a full path name. $FileName = basename($Filtee); for my $Obj (keys(%Objects)) { if ($Obj =~ /\/$FileName$/) { $Filtee = $Obj; last; } } if (!exists($Objects{$Filtee})) { next; } # Traverse the symbols of the filtee (these are # typically a smaller set than the filter) and if the # symbol is defined by the filter tag the symbol as a # filtee. for my $SymName (keys(%{$Objects{$Filtee}})) { my ($OFlag, $FFlag); # Ignore the usual stuff. if (($SymName =~ $MultSyms) || ($SymName =~ $CrtSyms)) { next; } if (!$Symbols{$SymName}{$Filter}) { next; } # Determine the type of filter. $OFlag = $Symbols{$SymName}{$Filter}[IX_OBJ_FLAG]; # Specifically identify the type of filtee we # have and remove any generic filtee flag. if ($OFlag & (S_F_OSFT | S_F_SSFT)) { $FFlag = S_F_SFTE; } else { $FFlag = S_F_AFTE; } $Symbols{$SymName}{$Filtee}[IX_OBJ_FLAG] |= $FFlag; $Symbols{$SymName}{$Filtee}[IX_OBJ_FLAG] &= ~S_F_GFTE; } } } # Traverse the set of per-symbol filters making sure we've tagged any # associated filtee symbols, as we did above for object filters. for my $Filtee (keys(%SymFltes)) { my ($FullPath) = $Filtee; my ($FileName); # Reduce the filtee to a simple file name. Then, try and # associate this simple file name with the objects that have # been processed. These objects are typically recorded with a # full path name. $FileName = basename($Filtee); for my $Obj (keys(%Objects)) { if ($Obj =~ /\/$FileName$/) { $FullPath = $Obj; last; } } if (!exists($Objects{$FullPath})) { next; } for my $SymName (keys(%{$SymFltes{$Filtee}})) { my ($OFlag, $FFlag); # Determine the type of filter. $OFlag = $SymFltes{$Filtee}{$SymName}[IX_SFTE_FLAG]; # Specifically identify the type of filtee we have and # remove any generic filtee flag. if ($OFlag & S_F_SSFT) { $FFlag = S_F_SFTE; } else { $FFlag = S_F_AFTE; } $Symbols{$SymName}{$FullPath}[IX_OBJ_FLAG] |= $FFlag; $Symbols{$SymName}{$FullPath}[IX_OBJ_FLAG] &= ~S_F_GFTE; } } # Process objects and their symbols as required. if ($opt{m}) { # If we're creating a mapfile, traverse each object we've # collected. foreach my $Obj (keys(%Objects)) { my ($File, $Path); # Skip any objects that should be ignored. if ($Obj =~ $Rtld) { next; } # Skip any versioned objects if required. if ($opt{v} && $Versioned{$Obj}) { next; } # Open the mapfile if required. $File = basename($Obj); $Path = "$DestDir/mapfile-$File"; if (!open(MAPOUT, "> $Path")) { printf STDERR gettext("%s: %s: open failed:" . "%s\n"), $Prog, $Path, $!; exit 1; } # Establish the mapfile preamble. print MAPOUT "#\n# Interface Definition mapfile for:\n"; if ($LariType == LARI_T_KERNEL) { print MAPOUT "#\tKernel Module: $Obj\n"; } else { print MAPOUT "#\tDynamic Object: $Obj\n"; print MAPOUT "#\tProcess: $File\n#\n\n"; } # Process each global symbol. print MAPOUT "$File {\n\tglobal:\n"; foreach my $SymName (sort(keys(%{$Objects{$Obj}}))) { my ($Flag) = $Objects{$Obj}{$SymName}; # For the first pass we're only interested in # symbols that: # # - Have been bound to from an # external object. # - Need to be global to enable a binding # to an interposing definition. # - Are needed by krtld. # # Skip self bindings, as these are candidates # for demoting to local. if (!($Flag & (S_F_EXTN | S_F_INTP | S_F_KRTLD))) { next; } if (($Flag & (S_F_EXTN | S_F_SELF | S_F_KRTLD)) == S_F_SELF) { next; } # Add the demangled name as a comment if # required. if ($opt{C}) { my ($DemName) = Demangle($SymName); if ($DemName ne "") { print MAPOUT "\t\t#$DemName\n"; } } print MAPOUT "\t\t$SymName;\n"; } # Process each local demotion. print MAPOUT "\tlocal:\n"; if ($opt{o}) { foreach my $SymName (sort(keys(%{$Objects{$Obj}}))) { my ($Flag) = $Objects{$Obj}{$SymName}; # For this pass we're only interested # in symbol definitions that haven't # been bound to, or have only been # bound to from the same object. if ($Flag & (S_F_EXTN | S_F_KRTLD)) { next; } # Add the demangled name as a comment if # required. if ($opt{C}) { my ($DemName) = Demangle($SymName); if ($DemName ne "") { print MAPOUT "\t\t#$DemName\n"; } } print MAPOUT "\t\t$SymName;\n"; } } # Capture everything else as local. print MAPOUT "\t\t\*;\n};\n"; close MAPOUT; } } else { # If we're gathering information regarding the symbols used by # the process, automatically sort any standard output using the # symbol name. if (!open(SORT, "| sort +1")) { printf STDERR gettext("%s: fork failed: %s\n"), $Prog, $!; exit 1; } foreach my $SymName (keys(%Symbols)) { my ($Cnt); # If we're looking for interesting symbols, inspect # each definition of each symbol. If one is found to # be interesting, the whole family are printed. if (($Cnt = Interesting($LariType, $SymName)) == 0) { next; } # We've found something interesting, or all symbols # should be output. List all objects that define this # symbol. foreach my $Obj (keys(%{$Symbols{$SymName}})) { my ($DemName, $Type); my ($Flag) = $Symbols{$SymName}{$Obj}[IX_OBJ_FLAG]; my ($Str) = "$Cnt:"; my ($Vis); my ($DisVis) = ""; # Do we just want overhead symbols. Consider # copy-relocations, rejections, and plt address # binding, as overhead too. if ($opt{o} && (($Flag & (S_F_REJT | S_F_EXTN | S_F_CPYR | S_F_PLTA)) == S_F_EXTN)) { next; } # Do we just want all symbols that have been # bound to. if (($opt{a} || $opt{o}) && $opt{b} && (($Flag & (S_F_EXTN | S_F_SELF | S_F_PROT)) == 0)) { next; } # If we haven't been asked for all symbols, only # print those reserved symbols that have been # bound to, as the number of reserved symbols # can be quite excessive. Also, remove any # standard filters, as nothing can bind to these # symbols anyway, provided they have not # contributed to a rejected binding. if (!$opt{a} && ((($SymName =~ $MultSyms) && (($Flag & (S_F_EXTN | S_F_SELF)) == 0)) || (($SymName =~ $CrtSyms) && (($Flag & (S_F_EXTN | S_F_SELF | S_F_PROT)) == 0)) || (($Flag & (S_F_SSFT | S_F_OSFT)) && (($Flag & S_F_REJT) == 0)))) { next; } # Skip any versioned objects if required. if ($opt{v} && $Versioned{$Obj}) { next; } # Display this symbol. if ($Symbols{$SymName}{$Obj}[IX_OBJ_REF]) { $Str = $Str . $Symbols{$SymName}{$Obj} [IX_OBJ_REF]; } else { $Str = $Str . '0'; } # Has the symbol been bound to externally if ($Flag & S_F_EXTN) { $Str = $Str . 'E'; } # Has the symbol been bound to from the same # object. if ($Flag & S_F_SELF) { $Str = $Str . 'S'; } # Has the symbol been bound to directly. if ($Flag & S_F_DIRC) { $Str = $Str . 'D'; } # Does this symbol originate for an explicit # interposer. if ($Flag & S_F_INTP) { $Str = $Str . 'I'; } # Is this symbol the reference data of a copy # relocation. if ($Flag & S_F_CPYR) { $Str = $Str . 'C'; } # Is this symbol part of filtee. if ($Flag & (S_F_SFTE | S_F_AFTE | S_F_GFTE)) { $Str = $Str . 'F'; } # Is this symbol protected (in which case there # may be a symbolic binding within the same # object to this symbol). if ($Flag & S_F_PROT) { $Str = $Str . 'P'; } # Is this symbol an executables .plt address. if ($Flag & S_F_PLTA) { $Str = $Str . 'A'; } # Does this binding originate from a user # (dlsym) request. if ($Flag & S_F_USER) { $Str = $Str . 'U'; } # Does this definition redirect the binding. if ($Flag & S_M_FILT) { $Str = $Str . 'R'; } # Does this definition explicitly define no # direct binding. if ($Flag & S_F_NODI) { $Str = $Str . 'N'; } # Was a binding to this definition rejected at # some point. if ($Flag & S_F_REJT) { $Str = $Str . 'r'; } # Is this definition a kernel modstub. if ($Flag & S_F_MODSTUB) { $Str = $Str . 'M'; } # Determine whether this is a function or a data # object. For the latter, display the symbol # size. Otherwise, the symbol is a reserved # label, and is left untyped. if ($Flag & S_F_FUNC) { $Type = '()'; } elsif ($Flag & S_F_OBJT) { $Type = '[' . $Symbols{$SymName}{$Obj} [IX_OBJ_SIZE] . ']'; } else { $Type = ""; } # Demangle the symbol name if desired. $DemName = Demangle($SymName); # If symbol visibility differences are # interesting, append the verbose representation # of any interesting visibilities. $Vis = $Symbols{$SymName}{$Obj}[IX_OBJ_VIS]; if ($opt{V} && $Vis) { if ($Vis =~ 'S') { $DisVis = " (singleton)"; } elsif ($Vis =~ 'P') { $DisVis = " (protected)"; } } if ($Mult) { print SORT " [$Str]: " . "$SymName$Type$DemName: " . "$Obj$DisVis\n"; } else { print SORT "[$Str]: " . "$SymName$Type$DemName: " . "$Obj$DisVis\n"; } } } close SORT; } } ## Interesting(LariType, SymName) # # Heuristics to determine whether a symbol binding is interesting. In most # applications there can be a large amount of symbol binding information to # wade through. The most typical binding, to a single definition, probably # isn't interesting or the cause of unexpected behavior. Here, we try and # determine those bindings that may can cause unexpected behavior. # # Note, this routine is actually called for all symbols so that their count # can be calculated in one place. # sub Interesting { my ($LariType, $SymName) = @_; my ($ObjCnt, $GFlags, $BndCnt, $FltCnt, $NodiCnt, $RdirCnt, $ExRef); my ($RejCnt, $TotCnt); # Scan all definitions of this symbol, thus determining the definition # count, the number of filters, redirections, executable references # (copy-relocations, or plt addresses), no-direct bindings, and the # number of definitions that have been bound to. $ObjCnt = $GFlags = $BndCnt = $FltCnt = $NodiCnt = $RdirCnt = $ExRef = $RejCnt = $TotCnt = 0; foreach my $Obj (keys(%{$Symbols{$SymName}})) { my ($Flag) = $Symbols{$SymName}{$Obj}[IX_OBJ_FLAG]; $TotCnt++; # Ignore standard filters when determining the symbol count, as # a standard filter can never be bound to. if (($Flag & (S_F_OSFT | S_F_SSFT)) == 0) { $ObjCnt++; } # If we're only looking at interesting objects, then standard # filters are ignored, so suppress any standard filtee tagging. if (!$opt{a}) { $Flag = $Symbols{$SymName}{$Obj}[IX_OBJ_FLAG] &= ~S_F_SFTE; } $GFlags |= $Flag; if ($Flag & (S_F_SFTE | S_F_AFTE | S_F_GFTE)) { $FltCnt++; } if ($Flag & S_F_NODI) { $NodiCnt++; } if ($Flag & (S_F_CPYR | S_F_PLTA)) { $ExRef++; } if ($Flag & S_M_FILT) { $RdirCnt++; } if ($Flag & S_F_REJT) { $RejCnt++; } # Ignore bindings to undefined .plts, and copy-relocation # references. These are implementation details, rather than # truly interesting multiple-binding. If a symbol is tagged # as protected, count it as having bound to itself, even though # we can't tell if it has really been used. if (($Flag & (S_F_SELF | S_F_EXTN | S_F_PROT)) && (($Flag & (S_F_PLTA | S_F_CPYR)) == 0)) { $BndCnt++; } } # If we want all overhead symbols, return the count. if ($opt{o}) { return $ObjCnt; } # If we want all symbols, return the count. If we want all bound # symbols, return the count provided it is non-zero. if ($opt{a} && (!$opt{b} || ($BndCnt > 0))) { return $TotCnt; } # Any rejected symbol is interesting if ($RejCnt) { return $TotCnt; } # Single instance symbol definitions aren't very interesting. if ($ObjCnt == 1) { return 0; } # Traverse each symbol definition looking for the following: # # . Multiple symbols are bound to externally. # . A symbol is bound to externally, and possibly symbolically. # # Two symbol bindings are acceptable in some cases, and thus aren't # interesting: # # . Copy relocations. Here, the executable binds to a shared object # to access the data definition, which is then copied to the # executable. All other references should then bind to the copied # data. # . Non-plt relocations to functions that are referenced by the # executable will bind to the .plt in the executable. This # provides for address comparison calculations (although plainly # an overhead). # . Kernel modstub symbols. In a scenario where a kernel module # pulls in other kernel modules, some of these dependencies # might bind to the actual definitions for symbols that also # have modstubs in the kernel, while other dependencies bind # to the modstub. This doesn't cause problems, and is therefore # not interesting. # # Multiple symbol bindings are acceptable in some cases, and thus aren't # interesting: # # . Filtees. Multiple filtees may exist for one filter. # if ((($ObjCnt == 2) && ($GFlags & (S_F_CPYR | S_F_PLTA | S_F_MODSTUB))) || ($ObjCnt == ($FltCnt + 1))) { return 0; } # Special handling for reserved symbols if ($LariType == LARI_T_KERNEL) { # Don't display the special kernel module reserved symbols # unless they've been bound to. Every module has these, and # they're unrelated to each other. They are intended to be # called by the kernel runtime loader (krtld), and not by other # modules, so bindings would be interesting. if (($GFlags & S_F_KRTLD) && ($BndCnt == 0)) { return (0); } } else { # Only display any reserved symbols in userland objects if # more than one binding has occurred. if ((($SymName =~ $MultSyms) || ($SymName =~ $CrtSyms)) && ($BndCnt < 2)) { return (0); } } # For all other symbols, determine whether a binding has occurred. # Note: definitions within an executable are tagged as protected ("P") # as they may have been bound to from within the executable - we can't # tell. if ($opt{b} && ($BndCnt == 0)) { return (0); } # Multiple instances of a definition, where all but one are filter # references and/or copy relocations, are also uninteresting. # Effectively, only one symbol is providing the final binding. if (($FltCnt && $RdirCnt) && (($FltCnt + $RdirCnt + $ExRef) == $ObjCnt)) { return (0); } # Multiple instances of explicitly defined no-direct binding symbols # are known to occur, and their no-binding definition indicates they # are expected and accounted for. Thus, these aren't interesting. if (($ExRef + $NodiCnt) == $ObjCnt) { return (0); } # We have an interesting symbol, returns its count. return $ObjCnt; } ## GetAllSymbols(Obj) # # Obtain the global symbol definitions of an object and determine whether the # object has been versioned. # sub GetAllSymbols { my ($Obj) = @_; my ($EType, $FileHandle); my (%AddrToName, %NameToAddr); my ($Exec) = 0; my ($Kmod) = 0; my ($Kernel) = 0; my ($Symb) = 0; my ($Copy) = 0; my ($Interpose) = 0; my ($Fltr) = 0; my ($Ehdr) = 0; my ($Dyn) = 0; my ($Rel) = 0; my ($Info) = 0; # Determine whether we've already retrieved this object's symbols. # Also, ignore the runtime linker: It is on a separate link-map, and # except for the filtee symbols that might be bound via libdl, is # uninteresting. Tag the runtime linker as versioned to simplify # possible -v processing. if ($Objects{$Obj}) { return; } if ($Obj =~ $Rtld) { $Versioned{$Obj} = 1; return; } # Get as much ELF information as we can from elfdump(1). A second # invocation of elfdump(1) is required to obtain the symbol table, whose # processing can be affected by states determined during this pass. # # The information required: # -e ELF header provides the file type # -d dynamic information provides filter names # -r relocations provide for copy relocations # -v object versioning # -y symbol information section provide pre-symbol filters # and direct binding information # # As this information can be quite large, process the elfdump(1) output # through a pipe. open($FileHandle, "LC_ALL=C elfdump -edrvy '$Obj' 2> /dev/null |"); while (defined(my $Line = <$FileHandle>)) { my (@Fields); chomp($Line); # Each collection of data is preceded with a title that # starts in column 0. Items of data all have some form of # indentation. if ($Line =~ /^[A-Z]/) { if ($Line =~ /^ELF Header/) { $Ehdr = 1; $Dyn = $Rel = $Info = 0; } elsif ($Line =~ /^Dynamic Section:/) { $Dyn = 1; $Ehdr = $Rel = $Info = 0; } elsif ($Line =~ /^Relocation Section:/) { $Rel = 1; $Ehdr = $Dyn = $Info = 0; } elsif ($Line =~ /^Syminfo Section:/) { $Info = 1; $Ehdr = $Dyn = $Rel = 0; } elsif ($Line =~ /^Version Definition Section:/) { # The existence of a VERDEF section is all we # are looking for. There is no need to parse # the specific version definitions. $Versioned{$Obj} = 1; $Ehdr = $Dyn = $Rel = $Info = 0; } else { $Ehdr = $Dyn = $Rel = $Info = 0; } next; } # Inspect the ELF header. if ($Ehdr eq 1) { # Determine the ELF file type from the e_type element. if ($Line =~ /e_type:/) { $EType = $Line; $EType =~ s/^\s+e_type:\s+(ET_[^\s]+)*$/$1/; if ($Line =~ /ET_EXEC/) { $Exec = 1; } # There's nothing of interest left in the ELF # header, so skip processing other entries. $Ehdr = 0; next; } } # Inspect the .dynamic section. if ($Dyn eq 1) { @Fields = split(' ', $Line); # Determine if the FILTER or AUXILIARY tag is set. if ($#Fields == 3) { my ($Flte) = 0; if ($Fields[1] eq 'FILTER') { $Fltr |= S_F_OSFT; $Flte = 1; } elsif ($Fields[1] eq 'AUXILIARY') { $Fltr |= S_F_OAFT; $Flte = 1; } if ($Flte eq 1) { my (@Filtees) = split(':', $Fields[3]); for my $Filtee (@Filtees) { if ($Filtee =~ $Rtld) { next; } $ObjFltrs{$Obj}{$Filtee} = 1; } } next; } # We're only interested in the FLAGS entry. if (($#Fields < 4) || ($Fields[1] !~ /^FLAGS/)) { next; } # Determine whether we've got a symbolicly bound object. # With newer link-editors, all symbols will be marked as # protected ("P"), but with older link-editors this # state could only be inferred from the symbolic dynamic # tag. if (($Fields[1] eq 'FLAGS') && ($Line =~ / SYMBOLIC /)) { $Symb = 1; next; } # Examine flags: # # OBJECT-INTERPOSE # Object is an interposer (userland). # # KMOD # Object is a kernel module. Note that lari does # not support the older definition of kernel # module as "a relocatable object with a dynamic # section". It must be explicitly linked with # -ztype=KMOD, which sets the MOD flag. # # NOHDR # 1st segment mapping omits the ELF header # and program headers. Such an object cannot # serve as a userland object. We treat it as # a watermark indicating the 'unix' kernel. if ($Fields[1] eq 'FLAGS_1') { if ($Line =~ / OBJECT-INTERPOSE /) { $Interpose = 1; } if (($EType eq 'ET_REL') && ($Line =~ / KMOD /)){ $Kmod = 1; $Exec = $Kernel = 0; } if ($Exec && ($Line =~ / NOHDR /)) { $Kernel = 1; $Exec = $Kmod = 0; } } next; } # Inspect the relocation information. As we're only looking # for copy relocations, this processing is only necessary for # executables. if ($Rel eq 1) { my ($SymName); if ($Exec == 0) { $Rel = 0; next; } # Obtain any copy relocations. if ($Line !~ / R_[A-Z0-9]+_COPY /) { next; } @Fields = split(' ', $Line); # Rel relocation records don't contain an addend, # while Rela do. 32-bit Intel is the only supported # Rel platform. if ($Fields[1] eq 'R_386_COPY') { $SymName = $Fields[5]; } else { $SymName = $Fields[6]; } $Symbols{$SymName}{$Obj}[IX_OBJ_FLAG] |= S_F_CPYR; $Objects{$Obj}{$SymName} |= S_F_CPYR; $Copy = 1; } # Inspect the .SUNW_syminfo section. # # The non-title syminfo output has the form: # # [index] [ flag flag flag ] dynndx boundto symbol # # where: # - There is no whitespace in [index], so split() will # see it as a single token. # - The flags are either a '0', or a list of names # within [] brackets, with whitespace between the # [] brackets and the flags. Hence, for non-zero tags, # the 2nd split token will be a '['. # - dynndx and boundto can both be empty. # - The symbol name is always the final field. # if ($Info eq 1) { my ($SymName); my ($Flags) = 0; @Fields = split(' ', $Line); my $LastField = $#Fields; # Binding attributes start in the second column, which # will be '[' if there are flags, or '0' otherwise. if (($LastField < 1) || ($Fields[1] ne '[')) { next; } for (my $i = 2; $i <= $LastField; $i++) { my $curFlag = $Fields[$i]; if ($curFlag eq ']') { last; } if ($curFlag eq 'NODIRECT') { $Flags |= S_F_NODI; next; } if (($curFlag eq 'FILTER') || ($curFlag eq 'WEAK')) { $Flags |= S_F_SSFT; next; } if ($curFlag eq 'AUXILIARY') { $Flags |= S_F_SAFT; next; } if ($curFlag eq 'INTERPOSE') { $Flags |= S_F_INTP; next; } } # Determine the symbol name based upon the number of # fields. if ($Flags) { $SymName = $Fields[$LastField]; $Symbols{$SymName}{$Obj}[IX_OBJ_FLAG] |= $Flags; $Objects{$Obj}{$SymName} |= $Flags; } # If this is a filter, we need to tag the associated # filtee symbol. However, the filtee might not have # been processed yet, so save the information for later. $Flags &= ~(S_F_NODI | S_F_INTP); if ($Flags) { my ($Filtee) = $Fields[$LastField - 1]; if ($Filtee =~ $Rtld) { next; } $SymFltes{$Filtee}{$SymName}[IX_SFTE_FLAG] = $Flags; } } } close($FileHandle); # If there's no expected information, it is possible we've been given # a debug output file and are processing the file from a location from # which the dependencies specified in the debug file aren't accessible. if ($Dyn eq 0) { printf STDERR gettext("%s: %s: unable to process ELF file\n"), $Prog, $Obj; # Add the file to our list, so that we don't create the same # message again. Processing should continue so that we can # flush out as many error messages as possible. $Objects{$Obj}{"DoesNotExist"} = 0; return; } # Process elfdump(1) once more to obtain the symbol table: # # - Read the .dynsym when one is present. We are only interested # in global symbols, so .SUNW_ldynsym is not needed. # # - Read the .symtab for kernel modules, as they don't have # a dynsym. # # If this is the unix kernel, make an extra prepass to find the value # of the stubs_base and stubs_end symbols that bracket all of the # modstubs entries. Such entries are placeholders for the actual # implementations of these symbols from other modules, and do not # represent multiple definitions, or actual symbol definitions. my $Symtab = $Kmod ? '.symtab' : '.dynsym'; my $Cmd = "LC_ALL=C elfdump -sN$Symtab '$Obj' 2> /dev/null |"; my $ModStubsBase = my $ModStubsEnd = 0; my $TestModStubs = 0; if ($Kernel) { # To do the modstubs identification, the running perl must # be 64-bit. Use Config{} to determine this. Perl as shipped # with Solaris is always 64-bit, but an end user might be # using one they build themselves. $TestModStubs = ($Config{ptrsize} >= 8); if ($TestModStubs) { open($FileHandle, $Cmd); while (defined(my $Line = <$FileHandle>)) { chomp($Line); if ($Line =~ /\]\s+([^\s]+)\s.*\s+stubs_(base|end)$/) { # For the scope of this if statement # only, disable warnings from hex() # about non-portable dependence # on 64-bit ints. no warnings 'portable'; my ($Addr, $SymName) = ($1, $2); if ($SymName eq 'base') { $ModStubsBase = hex($Addr); last if ($ModStubsEnd != 0); next; } if ($SymName eq 'end') { $ModStubsEnd = hex($Addr); last if ($ModStubsBase != 0); next; } } } close($FileHandle); if (($ModStubsBase == 0) || ($ModStubsEnd == 0) || ($ModStubsBase >= $ModStubsEnd)) { printf STDERR gettext("%s: unable to identify kernel " . "modstubs\n"), $Prog; $TestModStubs = 0; } } else { printf STDERR gettext("%s: warning: 32-bit perl cannot " . "evaluate kernel modstubs\n"), $Prog; } } open($FileHandle, $Cmd); while (defined(my $Line = <$FileHandle>)) { chomp($Line); my (@Fields) = split(' ', $Line); my ($Flags); # We're only interested in defined symbol entries. Unless # we've been asked for all symbols, ignore any ABS or NOTY # symbols. The former are typically reserved symbols or # versioning names. The latter are labels that are not bound # to. Note, ABS and NOTY symbols of non-zero size have been # known to occur, so capture them. if (($#Fields < 8) || ($Fields[4] !~ $GlobWeak) || ($Fields[7] eq 'UNDEF') || (!$opt{a} && (oct($Fields[2]) eq 0) && ((($Fields[7] eq 'ABS') && ($Fields[3] eq 'OBJT')) || ($Fields[3] eq 'NOTY')))) { next; } # If we're found copy relocations, save the address of all OBJT # definitions, together with the copy symbol. These definitions # are used to determine whether the copy symbol has any aliases # (ie. __iob and _iob). if (($Copy eq 1) && ($Fields[3] eq 'OBJT')) { push(@{$AddrToName{$Fields[1]}}, $Fields[8]); if (($Symbols{$Fields[8]}{$Obj}) && ($Symbols{$Fields[8]}{$Obj}[IX_OBJ_FLAG] & S_F_CPYR)) { $NameToAddr{$Fields[8]} = $Fields[1]; } } # Identify this symbol as global, and associate it with any # object filtering. $Flags = S_F_GLOB | $Fltr; # If this is the kernel object, identify MODSTUBS symbols. if ($TestModStubs) { no warnings 'portable'; # 64-bit perl required my $Addr = hex($Fields[1]); if (($Addr >= $ModStubsBase) && ($Addr <= $ModStubsEnd)) { $Flags |= S_F_MODSTUB; } } # If the symbol visibility is protected, this is an internal # symbolic binding. Treat symbols as having protected # visibility under the following conditions: # # - It has PROTECTED ('P') visibility explicitly set. # # - An INTERNAL visibility for a global symbol is invalid, # but for awhile ld(1) was setting this attribute # mistakenly for protected. Map 'I' to protected. # # - The dynamic section has the SYMBOLIC flag set # (see above). # # - A dynamic executable: These symbols can no more be # interposed on than symbols defined as protected within # shared objects, though for different reasons. # if (($Fields[5] =~ /^[IP]$/) || $Symb || $Exec) { $Flags |= S_F_PROT; } # If this object is marked as an interposer, tag each symbol. if ($Interpose) { $Flags |= S_F_INTP; } # Identify the symbol as a function or data type, and for the # latter, capture the symbol size. Ignore the standard symbolic # labels, as we don't want to type them. if ($Fields[8] !~ $MultSyms) { if ($Fields[3] =~ /^FUNC$/) { $Flags |= S_F_FUNC; } elsif ($Fields[3] =~ /^OBJT$/) { my ($Size) = $Fields[2]; if (oct($Size) eq 0) { $Size = "0"; } else { $Size =~ s/0x0*/0x/; } $Flags |= S_F_OBJT; $Symbols{$Fields[8]}{$Obj}[IX_OBJ_SIZE] = $Size; } } # If this is a kernel module, mark the special symbols called # by krtld if ($Kmod && ($Fields[8] =~ $KrtldSyms)) { $Flags |= S_F_KRTLD; } $Symbols{$Fields[8]}{$Obj}[IX_OBJ_FLAG] |= $Flags; $Symbols{$Fields[8]}{$Obj}[IX_OBJ_VIS] = $Fields[5]; $Objects{$Obj}{$Fields[8]} |= $Flags; } close($FileHandle); # Process any copy relocation symbols to see if the copy symbol has any # aliases, which should also be marked as copy relocations. if ($Copy) { foreach my $SymName (keys(%NameToAddr)) { my ($Addr) = $NameToAddr{$SymName}; # Determine all symbols that have the same address. foreach my $AliasName (@{$AddrToName{$Addr}}) { if ($SymName eq $AliasName) { next; } $Symbols{$AliasName}{$Obj}[IX_OBJ_FLAG] |= S_F_CPYR; $Objects{$Obj}{$AliasName} |= S_F_CPYR; } } } } # Demangle a symbol name if required. sub Demangle { my ($SymName) = @_; my ($DemName); if ($opt{C}) { my (@Dem); # Determine if we've already demangled this name. if (exists($DemSyms{$SymName})) { return $DemSyms{$SymName}; } @Dem = split(/\n/, `$DemPath '$SymName'`); foreach my $Line (@Dem) { my (@Fields) = split(' ', $Line); if (($#Fields < 2) || ($Fields[1] ne '==') || ($Fields[0] eq $Fields[2])) { next; } $DemName = $Line; $DemName =~ s/.*== (.*)$/ \[$1]/; $DemSyms{$SymName} = $DemName; return($DemName); } } $DemSyms{$SymName} = ""; return(""); }