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