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