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 2008 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, "... symbol `.*' ...". 588 while ($Fields[$Offset]) { 589 if ($Fields[$Offset] =~ /^\`(.*)\'$/) { 590 $SymName = $1; 591 $Offset++; 592 last; 593 } 594 $Offset++; 595 } 596 # Possible trailing binding info, "... (direct,...", or 597 # a rejection, "... (rejected - ...". 598 while ($Fields[$Offset]) { 599 if ($Fields[$Offset] =~ /^\((.*)/) { 600 $BndInfo = $1; 601 $Detail = 1; 602 $Offset++; 603 last; 604 } 605 $Offset++; 606 } 607 608 if ($Detail == 0) { 609 printf STDERR gettext("%s: %s: debug file " . 610 "does not contain `detail' information\n"), 611 $Prog, $DbgFile; 612 return; 613 } 614 615 # Collect the symbols from each object. 616 GetAllSymbols($RefFile); 617 GetAllSymbols($DstFile); 618 619 # Identify that this definition has been bound to. 620 $Symbols{$SymName}{$DstFile}[$ObjRef]++; 621 if ($RefFile eq $DstFile) { 622 # If the reference binds to a definition within 623 # the same file this symbol may be a candidate 624 # for reducing to local. 625 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Self; 626 $Objects{$DstFile}{$SymName} |= $Self; 627 } else { 628 # This symbol is required to satisfy an external 629 # reference. 630 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Extn; 631 $Objects{$DstFile}{$SymName} |= $Extn; 632 } 633 634 # Assign any other state indicated by the binding info 635 # associated with the diagnostic output. 636 if (!$BndInfo) { 637 next; 638 } 639 640 if ($BndInfo =~ /direct/) { 641 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Dirc; 642 $Objects{$DstFile}{$SymName} |= $Dirc; 643 } 644 if ($BndInfo =~ /copy-ref/) { 645 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Cpyr; 646 $Objects{$DstFile}{$SymName} |= $Cpyr; 647 } 648 if ($BndInfo =~ /filtee/) { 649 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Gfte; 650 $Objects{$DstFile}{$SymName} |= $Gfte; 651 } 652 if ($BndInfo =~ /interpose/) { 653 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Intp; 654 $Objects{$DstFile}{$SymName} |= $Intp; 655 } 656 if ($BndInfo =~ /plt-addr/) { 657 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Plta; 658 $Objects{$DstFile}{$SymName} |= $Plta; 659 } 660 if ($BndInfo =~ /rejected/) { 661 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $Rejt; 662 $Objects{$DstFile}{$SymName} |= $Rejt; 663 } 664 if ($Dlsym) { 665 $Symbols{$SymName}{$DstFile}[$ObjFlag] |= $User; 666 $Objects{$DstFile}{$SymName} |= $User; 667 } 668 } 669 } 670 close($FileHandle); 671 672 # Now that we've processed all objects, traverse the set of object 673 # filters that have been captured from parsing any FILTER and AUXILIARY 674 # dynamic tags. For each filtee, determine which of the symbols it 675 # exports are also defined in the filter. If a filter is bound to, the 676 # runtime linkers diagnostics will indicate a filtee binding. However, 677 # some of the filtee symbols may not be bound to, so here we mark them 678 # all so as to remove them from any interesting output. 679 for my $Filter (keys(%ObjFltrs)) { 680 681 # Determine the filtees that are associated with this filter. 682 for my $Filtee (keys(%{$ObjFltrs{$Filter}})) { 683 my ($FileName); 684 685 # Reduce the filtee to a simple file name. Then, try 686 # and associate this simple file name with the objects 687 # that have been processed. These objects are typically 688 # recorded with a full path name. 689 chomp($FileName = `basename $Filtee`); 690 for my $Obj (keys(%Objects)) { 691 if ($Obj =~ /\/$FileName$/) { 692 $Filtee = $Obj; 693 last; 694 } 695 } 696 697 if (!exists($Objects{$Filtee})) { 698 next; 699 } 700 701 # Traverse the symbols of the filtee (these are 702 # typically a smaller set than the filter) and if the 703 # symbol is defined by the filter tag the symbol as a 704 # filtee. 705 for my $SymName (keys(%{$Objects{$Filtee}})) { 706 my ($OFlag, $FFlag); 707 708 # Ignore the usual stuff. 709 if (($SymName =~ $MultSyms) || 710 ($SymName =~ $CrtSyms)) { 711 next; 712 } 713 714 if (!$Symbols{$SymName}{$Filter}) { 715 next; 716 } 717 718 # Determine the type of filter. 719 $OFlag = $Symbols{$SymName}{$Filter}[$ObjFlag]; 720 721 # Specifically identify the type of filtee we 722 # have and remove any generic filtee flag. 723 if ($OFlag & ($Osft | $Ssft)) { 724 $FFlag = $Sfte; 725 } else { 726 $FFlag = $Afte; 727 } 728 729 $Symbols{$SymName}{$Filtee}[$ObjFlag] |= $FFlag; 730 $Symbols{$SymName}{$Filtee}[$ObjFlag] &= ~$Gfte; 731 } 732 } 733 } 734 735 # Traverse the set of per-symbol filters making sure we've tagged any 736 # associated filtee symbols, as we did above for object filters. 737 for my $Filtee (keys(%SymFltes)) { 738 my ($FullPath) = $Filtee; 739 my ($FileName); 740 741 # Reduce the filtee to a simple file name. Then, try and 742 # associate this simple file name with the objects that have 743 # been processed. These objects are typically recorded with a 744 # full path name. 745 chomp($FileName = `basename $Filtee`); 746 for my $Obj (keys(%Objects)) { 747 if ($Obj =~ /\/$FileName$/) { 748 $FullPath = $Obj; 749 last; 750 } 751 } 752 753 if (!exists($Objects{$FullPath})) { 754 next; 755 } 756 757 for my $SymName (keys(%{$SymFltes{$Filtee}})) { 758 my ($OFlag, $FFlag); 759 760 # Determine the type of filter. 761 $OFlag = $SymFltes{$Filtee}{$SymName}[$SymFlag]; 762 763 # Specifically identify the type of filtee we have and 764 # remove any generic filtee flag. 765 if ($OFlag & $Ssft) { 766 $FFlag = $Sfte; 767 } else { 768 $FFlag = $Afte; 769 } 770 771 $Symbols{$SymName}{$FullPath}[$ObjFlag] |= $FFlag; 772 $Symbols{$SymName}{$FullPath}[$ObjFlag] &= ~$Gfte; 773 } 774 } 775 776 # Process objects and their symbols as required. 777 if ($opt{m}) { 778 # If we're creating a mapfile, traverse each object we've 779 # collected. 780 foreach my $Obj (keys(%Objects)) { 781 my ($File, $Path); 782 783 # Skip any objects that should be ignored. 784 if ($Obj =~ $Rtld) { 785 next; 786 } 787 788 # Skip any versioned objects if required. 789 if ($opt{v} && $Versioned{$Obj}) { 790 next; 791 } 792 793 # Open the mapfile if required. 794 $File = basename($Obj); 795 $Path = "$DestDir/mapfile-$File"; 796 if (!open(MAPOUT, "> $Path")) { 797 printf STDERR gettext("%s: %s: open failed:" . 798 "%s\n"), $Prog, $Path, $!; 799 exit 1; 800 } 801 802 # Establish the mapfile preamble. 803 print MAPOUT "#\n# Interface Definition mapfile for:\n"; 804 print MAPOUT "#\tDynamic Object: $Obj\n"; 805 print MAPOUT "#\tProcess: $File\n#\n\n"; 806 807 # Process each global symbol. 808 print MAPOUT "$File {\n\tglobal:\n"; 809 810 foreach my $SymName (sort(keys(%{$Objects{$Obj}}))) { 811 my ($Flag) = $Objects{$Obj}{$SymName}; 812 813 # For the first pass we're only interested in 814 # symbols that have been bound to from an 815 # external object, or must be global to enable 816 # a binding to an interposing definition. 817 # Skip bindings to ourself, as these are 818 # candidates for demoting to local. 819 if (!($Flag & ($Extn | $Intp))) { 820 next; 821 } 822 if (($Flag & ($Extn | $Self)) == $Self) { 823 next; 824 } 825 826 # Add the demangled name as a comment if 827 # required. 828 if ($opt{C}) { 829 my ($DemName) = Demangle($SymName); 830 831 if ($DemName ne "") { 832 print MAPOUT "\t\t#$DemName\n"; 833 } 834 } 835 print MAPOUT "\t\t$SymName;\n"; 836 } 837 838 # Process each local demotion. 839 print MAPOUT "\tlocal:\n"; 840 841 if ($opt{o}) { 842 foreach my $SymName 843 (sort(keys(%{$Objects{$Obj}}))) { 844 my ($Flag) = $Objects{$Obj}{$SymName}; 845 846 # For this pass we're only interested 847 # in symbol definitions that haven't 848 # been bound to, or have only been 849 # bound to from the same object. 850 if ($Flag & $Extn) { 851 next; 852 } 853 854 # Add the demangled name as a comment if 855 # required. 856 if ($opt{C}) { 857 my ($DemName) = 858 Demangle($SymName); 859 860 if ($DemName ne "") { 861 print MAPOUT 862 "\t\t#$DemName\n"; 863 } 864 } 865 print MAPOUT "\t\t$SymName;\n"; 866 } 867 } 868 869 # Capture everything else as local. 870 print MAPOUT "\t\t\*;\n};\n"; 871 close MAPOUT; 872 } 873 874 } else { 875 # If we're gathering information regarding the symbols used by 876 # the process, automatically sort any standard output using the 877 # symbol name. 878 if (!open(SORT, "| sort +1")) { 879 printf STDERR gettext("%s: fork failed: %s\n"), 880 $Prog, $!; 881 exit 1; 882 } 883 884 foreach my $SymName (keys(%Symbols)) { 885 my ($Cnt); 886 887 # If we're looking for interesting symbols, inspect 888 # each definition of each symbol. If one is found to 889 # be interesting, the whole family are printed. 890 if (($Cnt = Interesting($SymName)) == 0) { 891 next; 892 } 893 894 # We've found something interesting, or all symbols 895 # should be output. List all objects that define this 896 # symbol. 897 foreach my $Obj (keys(%{$Symbols{$SymName}})) { 898 my ($DemName, $Type); 899 my ($Flag) = $Symbols{$SymName}{$Obj}[$ObjFlag]; 900 my ($Str) = "$Cnt:"; 901 my ($Vis); 902 my ($DisVis) = ""; 903 904 # Do we just want overhead symbols. Consider 905 # copy-relocations, rejections, and plt address 906 # binding, as overhead too. 907 if ($opt{o} && (($Flag & 908 ($Rejt | $Extn | $Cpyr | $Plta)) == $Extn)) { 909 next; 910 } 911 912 # Do we just want all symbols that have been 913 # bound to. 914 if (($opt{a} || $opt{o}) && $opt{b} && 915 (($Flag & ($Extn | $Self | $Prot)) == 0)) { 916 next; 917 } 918 919 # If we haven't been asked for all symbols, only 920 # print those reserved symbols that have been 921 # bound to, as the number of reserved symbols 922 # can be quite excessive. Also, remove any 923 # standard filters, as nothing can bind to these 924 # symbols anyway, provided they have not 925 # contributed to a rejected binding. 926 if (!$opt{a} && ((($SymName =~ $MultSyms) && 927 (($Flag & ($Extn | $Self)) == 0)) || 928 (($SymName =~ $CrtSyms) && (($Flag & 929 ($Extn | $Self | $Prot)) == 0)) || 930 (($Flag & ($Ssft | $Osft)) && 931 (($Flag & $Rejt) == 0)))) { 932 next; 933 } 934 935 # Skip any versioned objects if required. 936 if ($opt{v} && $Versioned{$Obj}) { 937 next; 938 } 939 940 # Display this symbol. 941 if ($Symbols{$SymName}{$Obj}[$ObjRef]) { 942 $Str = $Str . 943 $Symbols{$SymName}{$Obj}[$ObjRef]; 944 } else { 945 $Str = $Str . '0'; 946 } 947 948 # Has the symbol been bound to externally 949 if ($Flag & $Extn) { 950 $Str = $Str . 'E'; 951 } 952 # Has the symbol been bound to from the same 953 # object. 954 if ($Flag & $Self) { 955 $Str = $Str . 'S'; 956 } 957 # Has the symbol been bound to directly. 958 if ($Flag & $Dirc) { 959 $Str = $Str . 'D'; 960 } 961 # Does this symbol originate for an explicit 962 # interposer. 963 if ($Flag & $Intp) { 964 $Str = $Str . 'I'; 965 } 966 # Is this symbol the reference data of a copy 967 # relocation. 968 if ($Flag & $Cpyr) { 969 $Str = $Str . 'C'; 970 } 971 # Is this symbol part of filtee. 972 if ($Flag & ($Sfte | $Afte | $Gfte)) { 973 $Str = $Str . 'F'; 974 } 975 # Is this symbol protected (in which case there 976 # may be a symbolic binding within the same 977 # object to this symbol). 978 if ($Flag & $Prot) { 979 $Str = $Str . 'P'; 980 } 981 # Is this symbol an executables .plt address. 982 if ($Flag & $Plta) { 983 $Str = $Str . 'A'; 984 } 985 # Does this binding originate from a user 986 # (dlsym) request. 987 if ($Flag & $User) { 988 $Str = $Str . 'U'; 989 } 990 # Does this definition redirect the binding. 991 if ($Flag & $Msft) { 992 $Str = $Str . 'R'; 993 } 994 # Does this definition explicitly define no 995 # direct binding. 996 if ($Flag & $Nodi) { 997 $Str = $Str . 'N'; 998 } 999 # Was a binding to this definition rejected at 1000 # some point. 1001 if ($Flag & $Rejt) { 1002 $Str = $Str . 'r'; 1003 } 1004 1005 # Determine whether this is a function or a data 1006 # object. For the latter, display the symbol 1007 # size. Otherwise, the symbol is a reserved 1008 # label, and is left untyped. 1009 if ($Flag & $Func) { 1010 $Type = '()'; 1011 } elsif ($Flag & $Objt) { 1012 $Type = '[' . 1013 $Symbols{$SymName}{$Obj}[$ObjSize] . 1014 ']'; 1015 } else { 1016 $Type = ""; 1017 } 1018 1019 # Demangle the symbol name if desired. 1020 $DemName = Demangle($SymName); 1021 1022 # If symbol visibility differences are 1023 # interesting, append the verbose representation 1024 # of any interesting visibilities. 1025 $Vis = $Symbols{$SymName}{$Obj}[$ObjVis]; 1026 if ($opt{V} && $Vis) { 1027 if ($Vis =~ 'S') { 1028 $DisVis = " (singleton)"; 1029 } elsif ($Vis =~ 'P') { 1030 $DisVis = " (protected)"; 1031 } 1032 } 1033 if ($Mult) { 1034 print SORT " [$Str]: " . 1035 "$SymName$Type$DemName: " . 1036 "$Obj$DisVis\n"; 1037 } else { 1038 print SORT "[$Str]: " . 1039 "$SymName$Type$DemName: " . 1040 "$Obj$DisVis\n"; 1041 } 1042 } 1043 } 1044 close SORT; 1045 } 1046} 1047 1048# Heuristics to determine whether a symbol binding is interesting. In most 1049# applications there can be a large amount of symbol binding information to 1050# wade through. The most typical binding, to a single definition, probably 1051# isn't interesting or the cause of unexpected behavior. Here, we try and 1052# determine those bindings that may can cause unexpected behavior. 1053# 1054# Note, this routine is actually called for all symbols so that their count 1055# can be calculated in one place. 1056sub Interesting 1057{ 1058 my ($SymName) = @_; 1059 my ($ObjCnt, $GFlags, $BndCnt, $FltCnt, $NodiCnt, $RdirCnt, $ExRef); 1060 my ($RejCnt, $TotCnt); 1061 1062 # Scan all definitions of this symbol, thus determining the definition 1063 # count, the number of filters, redirections, executable references 1064 # (copy-relocations, or plt addresses), no-direct bindings, and the 1065 # number of definitions that have been bound to. 1066 $ObjCnt = $GFlags = $BndCnt = $FltCnt = 1067 $NodiCnt = $RdirCnt = $ExRef = $RejCnt = $TotCnt = 0; 1068 foreach my $Obj (keys(%{$Symbols{$SymName}})) { 1069 my ($Flag) = $Symbols{$SymName}{$Obj}[$ObjFlag]; 1070 1071 $TotCnt++; 1072 1073 # Ignore standard filters when determining the symbol count, as 1074 # a standard filter can never be bound to. 1075 if (($Flag & ($Osft | $Ssft)) == 0) { 1076 $ObjCnt++; 1077 } 1078 1079 # If we're only looking at interesting objects, then standard 1080 # filters are ignored, so suppress any standard filtee tagging. 1081 if (!$opt{a}) { 1082 $Flag = $Symbols{$SymName}{$Obj}[$ObjFlag] &= ~$Sfte; 1083 } 1084 1085 $GFlags |= $Flag; 1086 if ($Flag & ($Sfte | $Afte | $Gfte)) { 1087 $FltCnt++; 1088 } 1089 if ($Flag & $Nodi) { 1090 $NodiCnt++; 1091 } 1092 if ($Flag & ($Cpyr | $Plta)) { 1093 $ExRef++; 1094 } 1095 if ($Flag & $Msft) { 1096 $RdirCnt++; 1097 } 1098 if ($Flag & $Rejt) { 1099 $RejCnt++; 1100 } 1101 1102 # Ignore bindings to undefined .plts, and copy-relocation 1103 # references. These are implementation details, rather than 1104 # a truly interesting multiple-binding. If a symbol is tagged 1105 # as protected, count it as having bound to itself, even though 1106 # we can't tell if it's really been used. 1107 if (($Flag & ($Self | $Extn | $Prot)) && 1108 (($Flag & ($Plta | $Cpyr)) == 0)) { 1109 $BndCnt++; 1110 } 1111 } 1112 1113 # If we want all overhead symbols, return the count. 1114 if ($opt{o}) { 1115 return $ObjCnt; 1116 } 1117 1118 # If we want all symbols, return the count. If we want all bound 1119 # symbols, return the count provided it is non-zero. 1120 if ($opt{a} && (!$opt{b} || ($BndCnt > 0))) { 1121 return $TotCnt; 1122 } 1123 1124 # Any rejected symbol is interesting 1125 if ($RejCnt) { 1126 return $TotCnt; 1127 } 1128 1129 # Single instance symbol definitions aren't very interesting. 1130 if ($ObjCnt == 1) { 1131 return 0; 1132 } 1133 1134 # Traverse each symbol definition looking for the following: 1135 # 1136 # . Multiple symbols are bound to externally. 1137 # . A symbol is bound to externally, and possibly symbolically. 1138 # 1139 # Two symbol bindings are acceptable in some cases, and thus aren't 1140 # interesting: 1141 # 1142 # . Copy relocations. Here, the executable binds to a shared object 1143 # to access the data definition, which is then copied to the 1144 # executable. All other references should then bind to the copied 1145 # data. 1146 # . Non-plt relocations to functions that are referenced by the 1147 # executable will bind to the .plt in the executable. This 1148 # provides for address comparison calculations (although plainly 1149 # an overhead). 1150 # 1151 # Multiple symbol bindings are acceptable in some cases, and thus aren't 1152 # interesting: 1153 # 1154 # . Filtees. Multiple filtees may exist for one filter. 1155 # 1156 if ((($ObjCnt == 2) && ($GFlags & ($Cpyr | $Plta))) || 1157 ($ObjCnt == ($FltCnt + 1))) { 1158 return 0; 1159 } 1160 1161 # Only display any reserved symbols if more than one binding has 1162 # occurred. 1163 if ((($SymName =~ $MultSyms) || ($SymName =~ $CrtSyms)) && 1164 ($BndCnt < 2)) { 1165 return (0); 1166 } 1167 1168 # For all other symbols, determine whether a binding has occurred. 1169 # Note: definitions within an executable are tagged as protected ("P") 1170 # as they may have been bound to from within the executable - we can't 1171 # tell. 1172 if ($opt{b} && ($BndCnt == 0)) { 1173 return (0); 1174 } 1175 1176 # Multiple instances of a definition, where all but one are filter 1177 # references and/or copy relocations, are also uninteresting. 1178 # Effectively, only one symbol is providing the final binding. 1179 if (($FltCnt && $RdirCnt) && 1180 (($FltCnt + $RdirCnt + $ExRef) == $ObjCnt)) { 1181 return (0); 1182 } 1183 1184 # Multiple instances of explicitly defined no-direct binding symbols 1185 # are known to occur, and their no-binding definition indicates they 1186 # are expected and accounted for. Thus, these aren't interesting. 1187 if (($ExRef + $NodiCnt) == $ObjCnt) { 1188 return (0); 1189 } 1190 1191 # We have an interesting symbol, returns its count. 1192 return $ObjCnt; 1193} 1194 1195# Obtain the global symbol definitions of an object and determine whether the 1196# object has been versioned. 1197sub GetAllSymbols { 1198 my ($Obj) = @_; 1199 my ($Type, $FileHandle); 1200 my (%AddrToName, %NameToAddr); 1201 my ($Exec) = 0; 1202 my ($Symb) = 0; 1203 my ($Copy) = 0; 1204 my ($Interpose) = 0; 1205 my ($Fltr) = 0; 1206 my ($Ehdr) = 0; 1207 my ($Dyn) = 0; 1208 my ($Rel) = 0; 1209 my ($Info) = 0; 1210 1211 # Determine whether we've already retrieved this object's symbols. 1212 # Also, ignore the runtime linker, it's on a separate link-map, and 1213 # except for the filtee symbols that might be bound via libdl, is 1214 # uninteresting. Tag the runtime linker as versioned to simplify 1215 # possible -v processing. 1216 if ($Objects{$Obj}) { 1217 return; 1218 } 1219 1220 if ($Obj =~ $Rtld) { 1221 $Versioned{$Obj} = 1; 1222 return; 1223 } 1224 1225 # Get as much ELF information as we can from elfdump(1). A second 1226 # invocation of elfdump(1) is required to obtain the symbol table, whose 1227 # processing can be affected by states determined during this pass. 1228 # 1229 # The information required: 1230 # -e ELF header provides the file type 1231 # -d dynamic information provides filter names 1232 # -r relocations provide for copy relocations 1233 # -v object versioning 1234 # -y symbol information section provide pre-symbol filters 1235 # and direct binding information 1236 # 1237 # As this information can be quite large, process the elfdump(1) output 1238 # through a pipe. 1239 open($FileHandle, "LC_ALL=C elfdump -edrvy '$Obj' 2> /dev/null |"); 1240 1241 while (defined(my $Line = <$FileHandle>)) { 1242 my (@Fields); 1243 1244 chomp($Line); 1245 1246 # Each collection of data is preceded with a title that 1247 # starts in column 0. Items of data all have some form of 1248 # indentation. 1249 if ($Line =~ /^[A-Z]/) { 1250 if ($Line =~ /^ELF Header/) { 1251 $Ehdr = 1; 1252 $Dyn = $Rel = $Info = 0; 1253 } elsif ($Line =~ /^Dynamic Section:/) { 1254 $Dyn = 1; 1255 $Ehdr = $Rel = $Info = 0; 1256 } elsif ($Line =~ /^Relocation Section:/) { 1257 $Rel = 1; 1258 $Ehdr = $Dyn = $Info = 0; 1259 } elsif ($Line =~ /^Syminfo Section:/) { 1260 $Info = 1; 1261 $Ehdr = $Dyn = $Rel = 0; 1262 } elsif ($Line =~ /^Version Definition Section:/) { 1263 # The existance of a VERDEF section is all we 1264 # are looking for. There is no need to parse 1265 # the specific version definitions. 1266 $Versioned{$Obj} = 1; 1267 $Ehdr = $Dyn = $Rel = $Info = 0; 1268 } else { 1269 $Ehdr = $Dyn = $Rel = $Info = 0; 1270 } 1271 next; 1272 } 1273 1274 # Inspect the ELF header. 1275 if ($Ehdr eq 1) { 1276 # Determine the ELF file type from the e_type element. 1277 if ($Line =~ /e_type:/) { 1278 if ($Line =~ /ET_EXEC/) { 1279 $Exec = 1; 1280 } 1281 1282 # There's nothing of interest left in the ELF 1283 # header, so skip processing other entries. 1284 $Ehdr = 0; 1285 next; 1286 } 1287 } 1288 1289 # Inspect the .dynamic section. 1290 if ($Dyn eq 1) { 1291 @Fields = split(' ', $Line); 1292 1293 # Determine if the FILTER or AUXILIARY tag is set. 1294 if ($#Fields == 3) { 1295 my ($Flte) = 0; 1296 1297 if ($Fields[1] eq 'FILTER') { 1298 $Fltr |= $Osft; 1299 $Flte = 1; 1300 } 1301 elsif ($Fields[1] eq 'AUXILIARY') { 1302 $Fltr |= $Oaft; 1303 $Flte = 1; 1304 } 1305 if ($Flte eq 1) { 1306 my (@Filtees) = split(':', $Fields[3]); 1307 1308 for my $Filtee (@Filtees) { 1309 if ($Filtee =~ $Rtld) { 1310 next; 1311 } 1312 $ObjFltrs{$Obj}{$Filtee} = 1; 1313 } 1314 } 1315 next; 1316 } 1317 1318 # We're only interested in the FLAGS entry. 1319 if (($#Fields < 4) || ($Fields[1] !~ /^FLAGS/)) { 1320 next; 1321 } 1322 1323 # Determine whether we've got a symbolicly bound object. 1324 # With newer link-editors, all symbols will be marked as 1325 # protected ("P"), but with older link-editors this 1326 # state could only be inferred from the symbolic dynamic 1327 # tag. 1328 if (($Fields[1] eq 'FLAGS') && 1329 ($Line =~ / SYMBOLIC /)) { 1330 $Symb = 1; 1331 next; 1332 } 1333 1334 # Determine whether this object is an interposer. 1335 if (($Fields[1] eq 'FLAGS_1') && 1336 ($Line =~ / OBJECT-INTERPOSE /)) { 1337 $Interpose = 1; 1338 next; 1339 } 1340 next; 1341 } 1342 1343 # Inspect the relocation information. As we're only looking 1344 # for copy relocations, this processing is only necessary for 1345 # executables. 1346 if ($Rel eq 1) { 1347 my ($SymName); 1348 1349 if ($Exec eq 0) { 1350 $Rel = 0; 1351 next; 1352 } 1353 1354 # Obtain any copy relocations. 1355 if ($Line !~ / R_[A-Z0-9]+_COPY /) { 1356 next; 1357 } 1358 1359 @Fields = split(' ', $Line); 1360 1361 # Intel relocation records don't contain an addend, 1362 # where as every other supported platform does. 1363 if ($Fields[0] eq 'R_386_COPY') { 1364 $SymName = $Fields[3]; 1365 } else { 1366 $SymName = $Fields[4]; 1367 } 1368 1369 $Symbols{$SymName}{$Obj}[$ObjFlag] |= $Cpyr; 1370 $Objects{$Obj}{$SymName} |= $Cpyr; 1371 $Copy = 1; 1372 } 1373 1374 # Inspect the .SUNW_syminfo section. 1375 if ($Info eq 1) { 1376 my ($SymName); 1377 my ($Flags) = 0; 1378 1379 @Fields = split(' ', $Line); 1380 1381 # Binding attributes are in the second column. 1382 if ($#Fields < 1) { 1383 next; 1384 } 1385 if ($Fields[1] =~ /N/) { 1386 $Flags |= $Nodi; 1387 } 1388 if ($Fields[1] =~ /F/) { 1389 $Flags |= $Ssft; 1390 } 1391 if ($Fields[1] =~ /A/) { 1392 $Flags |= $Saft; 1393 } 1394 if ($Fields[1] =~ /I/) { 1395 $Flags |= $Intp; 1396 } 1397 1398 # Determine the symbol name based upon the number of 1399 # fields. 1400 if ($Flags) { 1401 $SymName = $Fields[$#Fields]; 1402 $Symbols{$SymName}{$Obj}[$ObjFlag] |= $Flags; 1403 $Objects{$Obj}{$SymName} |= $Flags; 1404 } 1405 1406 # If this is a filter, we need to tag the associated 1407 # filtee symbol. However, the filtee might not have 1408 # been processed yet, so save this information for later. 1409 $Flags &= ~($Nodi | $Intp); 1410 if ($Flags) { 1411 my ($Filtee) = $Fields[$#Fields - 1]; 1412 1413 if ($Filtee =~ $Rtld) { 1414 next; 1415 } 1416 $SymFltes{$Filtee}{$SymName}[$SymFlag] = $Flags; 1417 } 1418 } 1419 } 1420 1421 close($FileHandle); 1422 1423 # If there's no expected information, it's possible we've been given a 1424 # debug output file and are processing the file from a location from 1425 # which the dependencies specified in the debug file aren't accessible. 1426 if ($Dyn eq 0) { 1427 printf STDERR gettext("%s: %s: unable to process ELF file\n"), 1428 $Prog, $Obj; 1429 1430 # Add the file to our list, so that we don't create the same 1431 # message again. Processing should continue so that we can 1432 # flush out as many error messages as possible. 1433 $Objects{$Obj}{"DoesNotExist"} = 0; 1434 return; 1435 } 1436 1437 # Process elfdump(1) once more to obtain the .dynsym symbol table. We 1438 # are only interested in global symbols, so .SUNW_ldynsym is not needed. 1439 open($FileHandle, "LC_ALL=C elfdump -sN.dynsym '$Obj' 2> /dev/null |"); 1440 1441 while (defined(my $Line = <$FileHandle>)) { 1442 chomp($Line); 1443 1444 my (@Fields) = split(' ', $Line); 1445 my ($Flags); 1446 1447 # We're only interested in defined symbol entries. Unless 1448 # we've been asked for all symbols, ignore any ABS or NOTY 1449 # symbols. The former are typically reserved symbols or 1450 # versioning names. The latter are labels that are not bound 1451 # to. Note, ABS and NOTY symbols of non-zero size have been 1452 # known to occur, so capture them. 1453 if (($#Fields < 8) || ($Fields[4] !~ $GlobWeak) || 1454 ($Fields[7] eq 'UNDEF') || 1455 (!$opt{a} && (oct($Fields[2]) eq 0) && 1456 ((($Fields[7] eq 'ABS') && ($Fields[3] eq 'OBJT')) || 1457 ($Fields[3] eq 'NOTY')))) { 1458 next; 1459 } 1460 1461 # If we're found copy relocations, save the address of all OBJT 1462 # definitions, together with the copy symbol. These definitions 1463 # are used to determine whether the copy symbol has any aliases 1464 # (ie. __iob and _iob). 1465 if (($Copy eq 1) && ($Fields[3] eq 'OBJT')) { 1466 push(@{$AddrToName{$Fields[1]}}, $Fields[8]); 1467 1468 if (($Symbols{$Fields[8]}{$Obj}) && 1469 ($Symbols{$Fields[8]}{$Obj}[$ObjFlag] & $Cpyr)) { 1470 $NameToAddr{$Fields[8]} = $Fields[1]; 1471 } 1472 } 1473 1474 # Identify this symbol as global, and associate it with any 1475 # object filtering. 1476 $Flags = $Glob | $Fltr; 1477 1478 # If the symbol visibility is protected, this is an internal 1479 # symbolic binding. Note, an INTERNAL visibility for a global 1480 # symbol is invalid, but for a while ld(1) was setting this 1481 # attribute mistakenly for protected. If this is a dynamic 1482 # executable, mark its symbols as protected. These symbols 1483 # can't be interposed on any more than symbols defined as 1484 # protected within shared objects). 1485 if (($Fields[5] =~ /^[IP]$/) || $Symb || $Exec) { 1486 $Flags |= $Prot; 1487 } 1488 1489 # If this object is marked as an interposer, tag each symbol. 1490 if ($Interpose) { 1491 $Flags |= $Intp; 1492 } 1493 1494 # Identify the symbol as a function or data type, and for the 1495 # latter, capture the symbol size. Ignore the standard symbolic 1496 # labels, as we don't want to type them. 1497 if ($Fields[8] !~ $MultSyms) { 1498 if ($Fields[3] =~ /^FUNC$/) { 1499 $Flags |= $Func; 1500 } elsif ($Fields[3] =~ /^OBJT$/) { 1501 my ($Size) = $Fields[2]; 1502 1503 if (oct($Size) eq 0) { 1504 $Size = "0"; 1505 } else { 1506 $Size =~ s/0x0*/0x/; 1507 } 1508 $Flags |= $Objt; 1509 $Symbols{$Fields[8]}{$Obj}[$ObjSize] = $Size; 1510 } 1511 } 1512 1513 $Symbols{$Fields[8]}{$Obj}[$ObjFlag] |= $Flags; 1514 $Symbols{$Fields[8]}{$Obj}[$ObjVis] = $Fields[5]; 1515 $Objects{$Obj}{$Fields[8]} |= $Flags; 1516 } 1517 close($FileHandle); 1518 1519 # Process any copy relocation symbols to see if the copy symbol has any 1520 # aliases, which should also be marked as copy relocations. 1521 if ($Copy) { 1522 foreach my $SymName (keys(%NameToAddr)) { 1523 my ($Addr) = $NameToAddr{$SymName}; 1524 1525 # Determine all symbols that have the same address. 1526 foreach my $AliasName (@{$AddrToName{$Addr}}) { 1527 if ($SymName eq $AliasName) { 1528 next; 1529 } 1530 $Symbols{$AliasName}{$Obj}[$ObjFlag] |= $Cpyr; 1531 $Objects{$Obj}{$AliasName} |= $Cpyr; 1532 } 1533 } 1534 } 1535} 1536 1537# Demangle a symbol name if required. 1538sub Demangle 1539{ 1540 my ($SymName) = @_; 1541 my ($DemName); 1542 1543 if ($opt{C}) { 1544 my (@Dem); 1545 1546 # Determine if we've already demangled this name. 1547 if (exists($DemSyms{$SymName})) { 1548 return $DemSyms{$SymName}; 1549 } 1550 1551 @Dem = split(/\n/, `dem '$SymName'`); 1552 foreach my $Line (@Dem) { 1553 my (@Fields) = split(' ', $Line); 1554 1555 if (($#Fields < 2) || ($Fields[1] ne '==') || 1556 ($Fields[0] eq $Fields[2])) { 1557 next; 1558 } 1559 $DemName = $Line; 1560 $DemName =~ s/.*== (.*)$/ \[$1]/; 1561 $DemSyms{$SymName} = $DemName; 1562 return($DemName); 1563 } 1564 } 1565 $DemSyms{$SymName} = ""; 1566 return(""); 1567} 1568