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