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