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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
  25 # Copyright 2015, Nexenta Systems Inc.
  26 #
  27 
  28 #
  29 # Check ELF information.
  30 #
  31 # This script descends a directory hierarchy inspecting ELF dynamic executables
  32 # and shared objects.  The general theme is to verify that common Makefile rules
  33 # have been used to build these objects.  Typical failures occur when Makefile
  34 # rules are re-invented rather than being inherited from "cmd/lib" Makefiles.
  35 #
  36 # As always, a number of components don't follow the rules, and these are
  37 # excluded to reduce this scripts output.
  38 #
  39 # By default any file that has conditions that should be reported is first
  40 # listed and then each condition follows.  The -o (one-line) option produces a
  41 # more terse output which is better for sorting/diffing with "nightly".
  42 #
  43 # NOTE: missing dependencies, symbols or versions are reported by running the
  44 # file through ldd(1).  As objects within a proto area are built to exist in a
  45 # base system, standard use of ldd(1) will bind any objects to dependencies
  46 # that exist in the base system.  It is frequently the case that newer objects
  47 # exist in the proto area that are required to satisfy other objects
  48 # dependencies, and without using these newer objects an ldd(1) will produce
  49 # misleading error messages.  To compensate for this, the -D/-d options, or the
  50 # existence of the CODEMSG_WS/ROOT environment variables, cause the creation of
  51 # alternative dependency mappings via crle(1) configuration files that establish
  52 # any proto shared objects as alternatives to their base system location.  Thus
  53 # ldd(1) can be executed against these configuration files so that objects in a
  54 # proto area bind to their dependencies in the same proto area.
  55 
  56 
  57 # Define all global variables (required for strict)
  58 use vars  qw($Prog $Env $Ena64 $Tmpdir $Adjunct $Proto);
  59 use vars  qw($LddNoU $Conf32 $Conf64);
  60 use vars  qw(%opt);
  61 use vars  qw($ErrFH $ErrTtl $InfoFH $InfoTtl $OutCnt1 $OutCnt2);
  62 
  63 # An exception file is used to specify regular expressions to match
  64 # objects. These directives specify special attributes of the object.
  65 # The regular expressions are read from the file and compiled into the
  66 # regular expression variables.
  67 #
  68 # The name of each regular expression variable is of the form
  69 #
  70 #       $EXRE_xxx
  71 #
  72 # where xxx is the name of the exception in lower case. For example,
  73 # the regular expression variable for EXEC_STACK is $EXRE_exec_stack.
  74 #
  75 # onbld_elfmod::LoadExceptionsToEXRE() depends on this naming convention
  76 # to initialize the regular expression variables, and to detect invalid
  77 # exception names.
  78 #
  79 # If a given exception is not used in the exception file, its regular
  80 # expression variable will be undefined. Users of these variables must
  81 # test the variable with defined() prior to use:
  82 #
  83 #       defined($EXRE_exec_stack) && ($foo =~ $EXRE_exec_stack)
  84 #
  85 # or if the test is to make sure the item is not specified:
  86 #
  87 #       !defined($EXRE_exec_stack) || ($foo !~ $EXRE_exec_stack)
  88 #
  89 # ----
  90 #
  91 # The exceptions are:
  92 #
  93 #   EXEC_DATA
  94 #       Objects that are not required to have non-executable writable
  95 #       data segments.
  96 #
  97 #   EXEC_STACK
  98 #       Objects that are not required to have a non-executable stack
  99 #
 100 #   NOCRLEALT
 101 #       Objects that should be skipped by AltObjectConfig() when building
 102 #       the crle script that maps objects to the proto area.
 103 #
 104 #    NODIRECT
 105 #       Objects that are not required to use direct bindings
 106 #
 107 #    NOSYMSORT
 108 #       Objects we should not check for duplicate addresses in
 109 #       the symbol sort sections.
 110 #
 111 #    OLDDEP
 112 #       Objects that are no longer needed because their functionalty
 113 #       has migrated elsewhere. These are usually pure filters that
 114 #       point at libc.
 115 #
 116 #    SKIP
 117 #       Files and directories that should be excluded from analysis.
 118 #
 119 #    STAB
 120 #       Objects that are allowed to contain stab debugging sections
 121 #
 122 #    TEXTREL
 123 #       Object for which relocations are allowed to the text segment
 124 #
 125 #    UNDEF_REF
 126 #       Objects that are allowed undefined references
 127 #
 128 #    UNREF_OBJ
 129 #       "unreferenced object=" ldd(1) diagnostics.
 130 #
 131 #    UNUSED_DEPS
 132 #       Objects that are allowed to have unused dependencies
 133 #
 134 #    UNUSED_OBJ
 135 #       Objects that are allowed to be unused dependencies
 136 #
 137 #    UNUSED_RPATH
 138 #       Objects with unused runpaths
 139 #
 140 
 141 use vars  qw($EXRE_exec_data $EXRE_exec_stack $EXRE_nocrlealt);
 142 use vars  qw($EXRE_nodirect $EXRE_nosymsort);
 143 use vars  qw($EXRE_olddep $EXRE_skip $EXRE_stab $EXRE_textrel $EXRE_undef_ref);
 144 use vars  qw($EXRE_unref_obj $EXRE_unused_deps $EXRE_unused_obj);
 145 use vars  qw($EXRE_unused_rpath);
 146 
 147 use strict;
 148 use Getopt::Std;
 149 use File::Basename;
 150 
 151 
 152 # Reliably compare two OS revisions.  Arguments are <ver1> <op> <ver2>.
 153 # <op> is the string form of a normal numeric comparison operator.
 154 sub cmp_os_ver {
 155         my @ver1 = split(/\./, $_[0]);
 156         my $op = $_[1];
 157         my @ver2 = split(/\./, $_[2]);
 158 
 159         push @ver2, ("0") x $#ver1 - $#ver2;
 160         push @ver1, ("0") x $#ver2 - $#ver1;
 161 
 162         my $diff = 0;
 163         while (@ver1 || @ver2) {
 164                 if (($diff = shift(@ver1) - shift(@ver2)) != 0) {
 165                         last;
 166                 }
 167         }
 168         return (eval "$diff $op 0" ? 1 : 0);
 169 }
 170 
 171 ## ProcFile(FullPath, RelPath, File, Class, Type, Verdef)
 172 #
 173 # Determine whether this a ELF dynamic object and if so investigate its runtime
 174 # attributes.
 175 #
 176 sub ProcFile {
 177         my($FullPath, $RelPath, $Class, $Type, $Verdef) = @_;
 178         my(@Elf, @Ldd, $Dyn, $Sym, $Stack);
 179         my($Sun, $Relsz, $Pltsz, $Tex, $Stab, $Strip, $Lddopt, $SymSort);
 180         my($Val, $Header, $IsX86, $RWX, $UnDep);
 181         my($HasDirectBinding);
 182 
 183         # Only look at executables and sharable objects
 184         return if ($Type ne 'EXEC') && ($Type ne 'DYN');
 185 
 186         # Ignore symbolic links
 187         return if -l $FullPath;
 188 
 189         # Is this an object or directory hierarchy we don't care about?
 190         return if (defined($EXRE_skip) && ($RelPath =~ $EXRE_skip));
 191 
 192         # Bail if we can't stat the file. Otherwise, note if it is SUID/SGID.
 193         return if !stat($FullPath);
 194         my $Secure = (-u _ || -g _) ? 1 : 0;
 195 
 196         # Reset output message counts for new input file
 197         $$ErrTtl = $$InfoTtl = 0;
 198 
 199         @Ldd = 0;
 200 
 201         # Determine whether we have access to inspect the file.
 202         if (!(-r $FullPath)) {
 203                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 204                     "unable to inspect file: permission denied");
 205                 return;
 206         }
 207 
 208         # Determine whether we have a executable (static or dynamic) or a
 209         # shared object.
 210         @Elf = split(/\n/, `elfdump -epdcy $FullPath 2>&1`);
 211 
 212         $Dyn = $Stack = $IsX86 = $RWX = 0;
 213         $Header = 'None';
 214         foreach my $Line (@Elf) {
 215                 # If we have an invalid file type (which we can tell from the
 216                 # first line), or we're processing an archive, bail.
 217                 if ($Header eq 'None') {
 218                         if (($Line =~ /invalid file/) ||
 219                             ($Line =~ /\Q$FullPath\E(.*):/)) {
 220                                 return;
 221                         }
 222                 }
 223 
 224                 if ($Line =~ /^ELF Header/) {
 225                         $Header = 'Ehdr';
 226                         next;
 227                 }
 228 
 229                 if ($Line =~ /^Program Header/) {
 230                         $Header = 'Phdr';
 231                         $RWX = 0;
 232                         next;
 233                 }
 234 
 235                 if ($Line =~ /^Dynamic Section/) {
 236                         # A dynamic section indicates we're a dynamic object
 237                         # (this makes sure we don't check static executables).
 238                         $Dyn = 1;
 239                         next;
 240                 }
 241 
 242                 if (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) {
 243                         # If it's a X86 object, we need to enforce RW- data.
 244                         $IsX86 = 1 if $Line =~ /(EM_AMD64|EM_386)/;
 245                         next;
 246                 }
 247 
 248                 if (($Header eq 'Phdr') &&
 249                     ($Line =~ /\[ PF_X\s+PF_W\s+PF_R \]/)) {
 250                         # RWX segment seen.
 251                         $RWX = 1;
 252                         next;
 253                 }
 254 
 255                 if (($Header eq 'Phdr') &&
 256                     ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) {
 257                         # Seen an RWX PT_LOAD segment.
 258                         if (!defined($EXRE_exec_data) ||
 259                             ($RelPath !~ $EXRE_exec_data)) {
 260                                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 261                                     "application requires non-executable " .
 262                                     "data\t<no -Mmapfile_noexdata?>");
 263                         }
 264                         next;
 265                 }
 266 
 267                 if (($Header eq 'Phdr') && ($Line =~ /\[ PT_SUNWSTACK \]/)) {
 268                         # This object defines a non-executable stack.
 269                         $Stack = 1;
 270                         next;
 271                 }
 272         }
 273 
 274         # Determine whether this ELF executable or shared object has a
 275         # conforming mcs(1) comment section.  If the correct $(POST_PROCESS)
 276         # macros are used, only a 3 or 4 line .comment section should exist
 277         # containing one or two "@(#)SunOS" identifying comments (one comment
 278         # for a non-debug build, and two for a debug build). The results of
 279         # the following split should be three or four lines, the last empty
 280         # line being discarded by the split.
 281         if ($opt{m}) {
 282                 my(@Mcs, $Con, $Dev);
 283 
 284                 @Mcs = split(/\n/, `mcs -p $FullPath 2>&1`);
 285 
 286                 $Con = $Dev = $Val = 0;
 287                 foreach my $Line (@Mcs) {
 288                         $Val++;
 289 
 290                         if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) {
 291                                 $Con = 1;
 292                                 last;
 293                         }
 294                         if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) {
 295                                 $Dev = 1;
 296                                 next;
 297                         }
 298                         if (($Dev == 0) && ($Val == 4)) {
 299                                 $Con = 1;
 300                                 last;
 301                         }
 302                         if (($Dev == 1) && ($Val == 5)) {
 303                                 $Con = 1;
 304                                 last;
 305                         }
 306                 }
 307                 if ($opt{m} && ($Con == 1)) {
 308                         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 309                     "non-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>");
 310                 }
 311         }
 312 
 313         # Applications should contain a non-executable stack definition.
 314         if (($Type eq 'EXEC') && ($Stack == 0) &&
 315             (!defined($EXRE_exec_stack) || ($RelPath !~ $EXRE_exec_stack))) {
 316                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 317                     "non-executable stack required\t<no -Mmapfile_noexstk?>");
 318         }
 319 
 320         # Having caught any static executables in the mcs(1) check and non-
 321         # executable stack definition check, continue with dynamic objects
 322         # from now on.
 323         if ($Dyn eq 0) {
 324                 return;
 325         }
 326 
 327         # Use ldd unless its a 64-bit object and we lack the hardware.
 328         if (($Class == 32) || $Ena64) {
 329                 my $LDDFullPath = $FullPath;
 330 
 331                 if ($Secure) {
 332                         # The execution of a secure application over an nfs file
 333                         # system mounted nosuid will result in warning messages
 334                         # being sent to /var/adm/messages.  As this type of
 335                         # environment can occur with root builds, move the file
 336                         # being investigated to a safe place first.  In addition
 337                         # remove its secure permission so that it can be
 338                         # influenced by any alternative dependency mappings.
 339         
 340                         my $File = $RelPath;
 341                         $File =~ s!^.*/!!;      # basename
 342 
 343                         my($TmpPath) = "$Tmpdir/$File";
 344 
 345                         system('cp', $LDDFullPath, $TmpPath);
 346                         chmod 0777, $TmpPath;
 347                         $LDDFullPath = $TmpPath;
 348                 }
 349 
 350                 # Use ldd(1) to determine the objects relocatability and use.
 351                 # By default look for all unreferenced dependencies.  However,
 352                 # some objects have legitimate dependencies that they do not
 353                 # reference.
 354                 if ($LddNoU) {
 355                         $Lddopt = "-ru";
 356                 } else {
 357                         $Lddopt = "-rU";
 358                 }
 359                 @Ldd = split(/\n/, `ldd $Lddopt $Env $LDDFullPath 2>&1`);
 360                 if ($Secure) {
 361                         unlink $LDDFullPath;
 362                 }
 363         }
 364 
 365         $Val = 0;
 366         $Sym = 5;
 367         $UnDep = 1;
 368 
 369         foreach my $Line (@Ldd) {
 370 
 371                 if ($Val == 0) {
 372                         $Val = 1;
 373                         # Make sure ldd(1) worked.  One possible failure is that
 374                         # this is an old ldd(1) prior to -e addition (4390308).
 375                         if ($Line =~ /usage:/) {
 376                                 $Line =~ s/$/\t<old ldd(1)?>/;
 377                                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl,
 378                                     $RelPath, $Line);
 379                                 last;
 380                         } elsif ($Line =~ /execution failed/) {
 381                                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl,
 382                                     $RelPath, $Line);
 383                                 last;
 384                         }
 385 
 386                         # It's possible this binary can't be executed, ie. we've
 387                         # found a sparc binary while running on an intel system,
 388                         # or a sparcv9 binary on a sparcv7/8 system.
 389                         if ($Line =~ /wrong class/) {
 390                                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 391                                     "has wrong class or data encoding");
 392                                 next;
 393                         }
 394 
 395                         # Historically, ldd(1) likes executable objects to have
 396                         # their execute bit set.
 397                         if ($Line =~ /not executable/) {
 398                                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 399                                     "is not executable");
 400                                 next;
 401                         }
 402                 }
 403 
 404                 # Look for "file" or "versions" that aren't found.  Note that
 405                 # these lines will occur before we find any symbol referencing
 406                 # errors.
 407                 if (($Sym == 5) && ($Line =~ /not found\)/)) {
 408                         if ($Line =~ /file not found\)/) {
 409                                 $Line =~ s/$/\t<no -zdefs?>/;
 410                         }
 411                         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
 412                         next;
 413                 }
 414                 # Look for relocations whose symbols can't be found.  Note, we
 415                 # only print out the first 5 relocations for any file as this
 416                 # output can be excessive.
 417                 if ($Sym && ($Line =~ /symbol not found/)) {
 418                         # Determine if this file is allowed undefined
 419                         # references.
 420                         if (($Sym == 5) && defined($EXRE_undef_ref) &&
 421                             ($RelPath =~ $EXRE_undef_ref)) {
 422                                 $Sym = 0;
 423                                 next;
 424                         }
 425                         if ($Sym-- == 1) {
 426                                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 427                                     "continued ...") if !$opt{o};
 428                                 next;
 429                         }
 430                         # Just print the symbol name.
 431                         $Line =~ s/$/\t<no -zdefs?>/;
 432                         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
 433                         next;
 434                 }
 435                 # Look for any unused search paths.
 436                 if ($Line =~ /unused search path=/) {
 437                         next if defined($EXRE_unused_rpath) &&
 438                             ($Line =~ $EXRE_unused_rpath);
 439 
 440                         if ($Secure) {
 441                                 $Line =~ s!$Tmpdir/!!;
 442                         }
 443                         $Line =~ s/^[ \t]*(.*)/\t$1\t<remove search path?>/;
 444                         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
 445                         next;
 446                 }
 447                 # Look for unreferenced dependencies.  Note, if any unreferenced
 448                 # objects are ignored, then set $UnDep so as to suppress any
 449                 # associated unused-object messages.
 450                 if ($Line =~ /unreferenced object=/) {
 451                         if (defined($EXRE_unref_obj) &&
 452                             ($Line =~ $EXRE_unref_obj)) {
 453                                 $UnDep = 0;
 454                                 next;
 455                         }
 456                         if ($Secure) {
 457                                 $Line =~ s!$Tmpdir/!!;
 458                         }
 459                         $Line =~ s/^[ \t]*(.*)/$1\t<remove lib or -zignore?>/;
 460                         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
 461                         next;
 462                 }
 463                 # Look for any unused dependencies.
 464                 if ($UnDep && ($Line =~ /unused/)) {
 465                         # Skip if object is allowed to have unused dependencies
 466                         next if defined($EXRE_unused_deps) &&
 467                             ($RelPath =~ $EXRE_unused_deps);
 468 
 469                         # Skip if dependency is always allowed to be unused
 470                         next if defined($EXRE_unused_obj) &&
 471                             ($Line =~ $EXRE_unused_obj);
 472 
 473                         $Line =~ s!$Tmpdir/!! if $Secure;
 474                         $Line =~ s/^[ \t]*(.*)/$1\t<remove lib or -zignore?>/;
 475                         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
 476                         next;
 477                 }
 478         }
 479 
 480         # Reuse the elfdump(1) data to investigate additional dynamic linking
 481         # information.
 482 
 483         $Sun = $Relsz = $Pltsz = $Dyn = $Stab = $SymSort = 0;
 484         $Tex = $Strip = 1;
 485         $HasDirectBinding = 0;
 486 
 487         $Header = 'None';
 488 ELF:    foreach my $Line (@Elf) {
 489                 # We're only interested in the section headers and the dynamic
 490                 # section.
 491                 if ($Line =~ /^Section Header/) {
 492                         $Header = 'Shdr';
 493 
 494                         if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) {
 495                                 # This object has a combined relocation section.
 496                                 $Sun = 1;
 497 
 498                         } elsif (($Stab == 0) && ($Line =~ /\.stab/)) {
 499                                 # This object contain .stabs sections
 500                                 $Stab = 1;
 501                         } elsif (($SymSort == 0) &&
 502                                  ($Line =~ /\.SUNW_dyn(sym)|(tls)sort/)) {
 503                                 # This object contains a symbol sort section
 504                                 $SymSort = 1;
 505                         }
 506 
 507                         if (($Strip == 1) && ($Line =~ /\.symtab/)) {
 508                                 # This object contains a complete symbol table.
 509                                 $Strip = 0;
 510                         }
 511                         next;
 512 
 513                 } elsif ($Line =~ /^Dynamic Section/) {
 514                         $Header = 'Dyn';
 515                         next;
 516                 } elsif ($Line =~ /^Syminfo Section/) {
 517                         $Header = 'Syminfo';
 518                         next;
 519                 } elsif (($Header ne 'Dyn') && ($Header ne 'Syminfo')) {
 520                         next;
 521                 }
 522 
 523                 # Look into the Syminfo section.
 524                 # Does this object have at least one Directly Bound symbol?
 525                 if (($Header eq 'Syminfo')) {
 526                         my(@Symword);
 527 
 528                         if ($HasDirectBinding == 1) {
 529                                 next;
 530                         }
 531 
 532                         @Symword = split(' ', $Line);
 533 
 534                         if (!defined($Symword[1])) {
 535                                 next;
 536                         }
 537                         if ($Symword[1] =~ /B/) {
 538                                 $HasDirectBinding = 1;
 539                         }
 540                         next;
 541                 }
 542 
 543                 # Does this object contain text relocations.
 544                 if ($Tex && ($Line =~ /TEXTREL/)) {
 545                         # Determine if this file is allowed text relocations.
 546                         if (defined($EXRE_textrel) &&
 547                             ($RelPath =~ $EXRE_textrel)) {
 548                                 $Tex = 0;
 549                                 next ELF;
 550                         }
 551                         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 552                             "TEXTREL .dynamic tag\t\t\t<no -Kpic?>");
 553                         $Tex = 0;
 554                         next;
 555                 }
 556 
 557                 # Does this file have any relocation sections (there are a few
 558                 # psr libraries with no relocations at all, thus a .SUNW_reloc
 559                 # section won't exist either).
 560                 if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) {
 561                         $Relsz = hex((split(' ', $Line))[2]);
 562                         next;
 563                 }
 564 
 565                 # Does this file have any plt relocations.  If the plt size is
 566                 # equivalent to the total relocation size then we don't have
 567                 # any relocations suitable for combining into a .SUNW_reloc
 568                 # section.
 569                 if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) {
 570                         $Pltsz = hex((split(' ', $Line))[2]);
 571                         next;
 572                 }
 573 
 574                 # Does this object have any dependencies.
 575                 if ($Line =~ /NEEDED/) {
 576                         my($Need) = (split(' ', $Line))[3];
 577 
 578                         if (defined($EXRE_olddep) && ($Need =~ $EXRE_olddep)) {
 579                                 # Catch any old (unnecessary) dependencies.
 580                                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 581                         "NEEDED=$Need\t<dependency no longer necessary>");
 582                         } elsif ($opt{i}) {
 583                                 # Under the -i (information) option print out
 584                                 # any useful dynamic entries.
 585                                 onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath,
 586                                     "NEEDED=$Need");
 587                         }
 588                         next;
 589                 }
 590 
 591                 # Is this object built with -B direct flag on?
 592                 if ($Line =~ / DIRECT /) {
 593                         $HasDirectBinding = 1;
 594                 }
 595 
 596                 # Does this object specify a runpath.
 597                 if ($opt{i} && ($Line =~ /RPATH/)) {
 598                         my($Rpath) = (split(' ', $Line))[3];
 599                         onbld_elfmod::OutMsg($InfoFH, $InfoTtl,
 600                             $RelPath, "RPATH=$Rpath");
 601                         next;
 602                 }
 603         }
 604 
 605         # A shared object, that contains non-plt relocations, should have a
 606         # combined relocation section indicating it was built with -z combreloc.
 607         if (($Type eq 'DYN') && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) {
 608                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 609                     ".SUNW_reloc section missing\t\t<no -zcombreloc?>");
 610         }
 611 
 612         # No objects released to a customer should have any .stabs sections
 613         # remaining, they should be stripped.
 614         if ($opt{s} && $Stab) {
 615                 goto DONESTAB if defined($EXRE_stab) && ($RelPath =~ $EXRE_stab);
 616 
 617                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 618                     "debugging sections should be deleted\t<no strip -x?>");
 619         }
 620 
 621         # Identify an object that is not built with either -B direct or
 622         # -z direct.
 623         goto DONESTAB
 624             if (defined($EXRE_nodirect) && ($RelPath =~ $EXRE_nodirect));
 625 
 626         if ($Relsz && ($HasDirectBinding == 0)) {
 627                 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 628                  "object has no direct bindings\t<no -B direct or -z direct?>");
 629         }
 630 
 631 DONESTAB:
 632 
 633         # All objects should have a full symbol table to provide complete
 634         # debugging stack traces.
 635         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 636             "symbol table should not be stripped\t<remove -s?>") if $Strip;
 637 
 638         # If there are symbol sort sections in this object, report on
 639         # any that have duplicate addresses.
 640         ProcSymSort($FullPath, $RelPath) if $SymSort;
 641 
 642         # If -v was specified, and the object has a version definition
 643         # section, generate output showing each public symbol and the
 644         # version it belongs to.
 645         ProcVerdef($FullPath, $RelPath)
 646             if ($Verdef eq 'VERDEF') && $opt{v};
 647 }
 648 
 649 
 650 ## ProcSymSortOutMsg(RelPath, secname, addr, names...)
 651 #
 652 # Call onbld_elfmod::OutMsg for a duplicate address error in a symbol sort
 653 # section
 654 #
 655 sub ProcSymSortOutMsg {
 656         my($RelPath, $secname, $addr, @names) = @_;
 657 
 658         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 659             "$secname: duplicate $addr: ". join(', ', @names));
 660 }
 661 
 662 
 663 ## ProcSymSort(FullPath, RelPath)
 664 #
 665 # Examine the symbol sort sections for the given object and report
 666 # on any duplicate addresses found.  Ideally, mapfile directives
 667 # should be used when building objects that have multiple symbols
 668 # with the same address so that only one of them appears in the sort
 669 # section. This saves space, reduces user confusion, and ensures that
 670 # libproc and debuggers always display public names instead of symbols
 671 # that are merely implementation details.
 672 #
 673 sub ProcSymSort {
 674 
 675         my($FullPath, $RelPath) = @_;
 676 
 677         # If this object is exempt from checking, return quietly
 678         return if defined($EXRE_nosymsort) && ($FullPath =~ $EXRE_nosymsort);
 679 
 680 
 681         open(SORT, "elfdump -S $FullPath|") ||
 682             die "$Prog: Unable to execute elfdump (symbol sort sections)\n";
 683 
 684         my $line;
 685         my $last_addr;
 686         my @dups = ();
 687         my $secname;
 688         while ($line = <SORT>) {
 689                 chomp $line;
 690                 
 691                 next if ($line eq '');
 692 
 693                 # If this is a header line, pick up the section name
 694                 if ($line =~ /^Symbol Sort Section:\s+([^\s]+)\s+/) {
 695                         $secname = $1;
 696 
 697                         # Every new section is followed by a column header line
 698                         $line = <SORT>;           # Toss header line
 699 
 700                         # Flush anything left from previous section
 701                         ProcSymSortOutMsg($RelPath, $secname, $last_addr, @dups)
 702                             if (scalar(@dups) > 1);
 703 
 704                         # Reset variables for new sort section
 705                         $last_addr = '';
 706                         @dups = ();
 707 
 708                         next;
 709                 }
 710 
 711                 # Process symbol line
 712                 my @fields = split /\s+/, $line;
 713                 my $new_addr = $fields[2]; 
 714                 my $new_type = $fields[8];
 715                 my $new_name = $fields[9]; 
 716 
 717                 if ($new_type eq 'UNDEF') {
 718                         onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
 719                             "$secname: unexpected UNDEF symbol " .
 720                             "(link-editor error): $new_name");
 721                         next;
 722                 }
 723 
 724                 if ($new_addr eq $last_addr) {
 725                         push @dups, $new_name;
 726                 } else {
 727                         ProcSymSortOutMsg($RelPath, $secname,
 728                             $last_addr, @dups) if (scalar(@dups) > 1);
 729                         @dups = ( $new_name );
 730                         $last_addr = $new_addr; 
 731                 }
 732         }
 733 
 734         ProcSymSortOutMsg($RelPath, $secname, $last_addr, @dups)
 735                 if (scalar(@dups) > 1);
 736         
 737         close SORT;
 738 }
 739 
 740 
 741 ## ProcVerdef(FullPath, RelPath)
 742 #
 743 # Examine the version definition section for the given object and report
 744 # each public symbol along with the version it belongs to.
 745 #
 746 sub ProcVerdef {
 747 
 748         my($FullPath, $RelPath) = @_;
 749         my $line;
 750         my $cur_ver = '';
 751         my $tab = $opt{o} ? '' : "\t";
 752 
 753         # pvs -dov provides information about the versioning hierarchy
 754         # in the file. Lines are of the format:
 755         #       path - version[XXX];
 756         # where [XXX] indicates optional information, such as flags
 757         # or inherited versions.
 758         #
 759         # Private versions are allowed to change freely, so ignore them.
 760         open(PVS, "pvs -dov $FullPath|") ||
 761             die "$Prog: Unable to execute pvs (version definition section)\n";
 762 
 763         while ($line = <PVS>) {
 764                 chomp $line;
 765 
 766                 if ($line =~ /^[^\s]+\s+-\s+([^;]+)/) {
 767                         my $ver = $1;
 768 
 769                         next if $ver =~ /private/i;
 770                         onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath,
 771                             "${tab}VERDEF=$ver");
 772                 }
 773         }
 774         close PVS;
 775 
 776         # pvs -dos lists the symbols assigned to each version definition.
 777         # Lines are of the format:
 778         #       path - version: symbol;
 779         #       path - version: symbol (size);
 780         # where the (size) is added to data items, but not for functions.
 781         # We strip off the size, if present.
 782 
 783         open(PVS, "pvs -dos $FullPath|") ||
 784             die "$Prog: Unable to execute pvs (version definition section)\n";
 785         while ($line = <PVS>) {
 786                 chomp $line;
 787                 if ($line =~ /^[^\s]+\s+-\s+([^:]+):\s*([^\s;]+)/) {
 788                     my $ver = $1;
 789                     my $sym = $2;
 790 
 791                     next if $ver =~ /private/i;
 792 
 793                     if ($opt{o}) {
 794                         onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath,
 795                             "VERSION=$ver, SYMBOL=$sym");
 796                     } else {
 797                         if ($cur_ver ne $ver) {
 798                             onbld_elfmod::OutMsg($InfoFH, $InfoTtl,
 799                                 $RelPath, "VERSION=$ver");
 800                             $cur_ver = $ver;
 801                         }                           
 802                         onbld_elfmod::OutMsg($InfoFH, $InfoTtl,
 803                             $RelPath, "SYMBOL=$sym");
 804                     }
 805                 }
 806         }
 807         
 808         close PVS;
 809 }
 810 
 811 
 812 ## OpenFindElf(file, FileHandleRef, LineNumRef)
 813 #
 814 # Open file in 'find_elf -r' format, and return the value of
 815 # the opening PREFIX line.
 816 #
 817 # entry:
 818 #       file - file, or find_elf child process, to open
 819 #       FileHandleRef - Reference to file handle to open
 820 #       LineNumRef - Reference to integer to increment as lines are input
 821 #
 822 # exit:
 823 #       This routine issues a fatal error and does not return on error.
 824 #       Otherwise, the value of PREFIX is returned.
 825 #
 826 sub OpenFindElf {
 827         my ($file, $fh, $LineNum) = @_;
 828         my $line;
 829         my $prefix;
 830 
 831         open($fh, $file) || die "$Prog: Unable to open: $file";
 832         $$LineNum = 0;
 833 
 834         # This script requires relative paths as created by 'find_elf -r'.
 835         # When this is done, the first non-comment line will always
 836         # be PREFIX. Obtain that line, or issue a fatal error.
 837         while ($line = onbld_elfmod::GetLine($fh, $LineNum)) {
 838                 if ($line =~ /^PREFIX\s+(.*)$/i) {
 839                         $prefix = $1;
 840                         last;
 841                 }
 842 
 843                 die "$Prog: No PREFIX line seen on line $$LineNum: $file";
 844         }
 845 
 846         $prefix;
 847 }
 848 
 849 
 850 ## ProcFindElf(file)
 851 #
 852 # Open the specified file, which must be produced by "find_elf -r",
 853 # and process the files it describes.
 854 #
 855 sub ProcFindElf {
 856         my $file = $_[0];
 857         my $line;
 858         my $LineNum;
 859 
 860         my $prefix = OpenFindElf($file, \*FIND_ELF, \$LineNum);
 861 
 862         while ($line = onbld_elfmod::GetLine(\*FIND_ELF, \$LineNum)) {
 863                 next if !($line =~ /^OBJECT\s/i);
 864 
 865                 my ($item, $class, $type, $verdef, $obj) =
 866                     split(/\s+/, $line, 5);
 867 
 868                 ProcFile("$prefix/$obj", $obj, $class, $type, $verdef);
 869         }
 870 
 871         close FIND_ELF;
 872 }
 873 
 874 
 875 ## AltObjectConfig(proto, adjunct)
 876 #
 877 # Recurse through a directory hierarchy looking for appropriate dependencies
 878 # to map from their standard system locations to the proto area via a crle
 879 # config file.
 880 #
 881 # entry:
 882 #       proto, adjunct - Files of ELF objects, in 'find_elf -r' format, to
 883 #       examine.
 884 #
 885 # exit:
 886 #       Scripts are generated for the 32 and 64-bit cases to run crle
 887 #       and create runtime configuration files that will establish
 888 #       alternative dependency mappings for the objects identified.
 889 #
 890 #       $Env - Set to environment variable definitions that will cause
 891 #               the config files generated by this routine to be used
 892 #               by ldd.
 893 #       $Conf32, $Conf64 - Undefined, or set to the config files generated
 894 #               by this routine. If defined, the caller is responsible for
 895 #               unlinking the files before exiting.
 896 #
 897 sub AltObjectConfig {
 898         my $file = $_[0];
 899         my ($Crle32, $Crle64);
 900         my $line;
 901         my $LineNum;
 902         my $obj_path;
 903         my $obj_active = 0;
 904         my $obj_class;
 905 
 906         my $prefix;
 907 FILE:
 908         while ($prefix = OpenFindElf($file, \*FIND_ELF)) {
 909         
 910         LINE:
 911                 while ($line = onbld_elfmod::GetLine(\*FIND_ELF, \$LineNum)) {
 912                       ITEM: {
 913 
 914                                 if ($line =~ /^OBJECT\s/i) {
 915                                         my ($item, $class, $type, $verdef, $obj)
 916                                             = split(/\s+/, $line, 5);
 917 
 918                                         if ($type eq 'DYN') {
 919                                                 $obj_active = 1;
 920                                                 $obj_path = $obj;
 921                                                 $obj_class = $class;
 922                                         } else {
 923                                                 # Only want sharable objects
 924                                                 $obj_active = 0;
 925                                         }
 926                                         last ITEM;
 927                                 }
 928 
 929                                 # We need to follow links to sharable objects
 930                                 # so that any dependencies are expressed in all
 931                                 # their available forms. We depend on ALIAS
 932                                 # lines directly following the object they
 933                                 # alias, so if we have a current object, this
 934                                 # alias belongs to it.
 935                                 if ($obj_active && ($line =~ /^ALIAS\s/i)) {
 936                                         my ($item, $real_obj, $obj) =
 937                                             split(/\s+/, $line, 3);
 938                                         $obj_path = $obj;
 939                                         last ITEM;
 940                                 }
 941 
 942                                 # Skip unrecognized item
 943                                 next LINE;
 944                         }
 945 
 946                         next if !$obj_active;
 947 
 948                         my $full = "$prefix/$obj_path";
 949 
 950                         next if defined($EXRE_nocrlealt) &&
 951                             ($obj_path =~ $EXRE_nocrlealt);
 952 
 953                         my $Dir = $full;
 954                         $Dir =~ s/^(.*)\/.*$/$1/;
 955 
 956                         # Create a crle(1) script for the dependency we've
 957                         # found. We build separate scripts for the 32 and 64-bit
 958                         # cases. We create and initialize each script when we
 959                         # encounter the first object that needs it.
 960                         if ($obj_class == 32) {
 961                                 if (!$Crle32) {
 962                                         $Crle32 = "$Tmpdir/$Prog.crle32.$$";
 963                                         open(CRLE32, "> $Crle32") || die
 964                                             "$Prog: open failed: $Crle32: $!";
 965                                         print CRLE32 "#!/bin/sh\ncrle \\\n";
 966                                 }
 967                                 print CRLE32 "\t-o $Dir -a /$obj_path \\\n";
 968                         } elsif ($Ena64) {
 969                                 if (!$Crle64) {
 970                                         $Crle64 = "$Tmpdir/$Prog.crle64.$$";
 971                                         open(CRLE64, "> $Crle64") || die
 972                                             "$Prog: open failed: $Crle64: $!";
 973                                         print CRLE64 "#!/bin/sh\ncrle -64\\\n";
 974                                 }
 975                                 print CRLE64 "\t-o $Dir -a /$obj_path \\\n";
 976                         }
 977                 }
 978 
 979                 close FIND_ELF;
 980                 if ($file eq $_[0]) {
 981                         $file = $_[1];
 982                         next FILE;
 983                 }
 984 
 985                 last FILE;
 986         }
 987 
 988         # Now that the config scripts are complete, use them to generate
 989         # runtime linker config files.
 990         $Adjunct //= "";
 991 
 992         if ($Crle64) {
 993                 print CRLE64
 994                         "\t-l ${Proto}/lib/64:${Proto}/usr/lib/64 \\\n";
 995 
 996                 print CRLE64
 997                     "\t-l ${Adjunct}/lib/64:${Adjunct}/usr/lib/64 \\\n";
 998 
 999                 $Conf64 = "$Tmpdir/$Prog.conf64.$$";
1000                 print CRLE64 "\t-c $Conf64\n";
1001 
1002                 chmod 0755, $Crle64;
1003                 close CRLE64;
1004 
1005                 undef $Conf64 if system($Crle64);
1006 
1007                 # Done with the script
1008                 unlink $Crle64;
1009         }
1010         if ($Crle32) {
1011                 print CRLE32 "\t-l ${Proto}/lib:${Proto}/usr/lib \\\n";
1012                 print CRLE32 "\t-l ${Adjunct}/lib:${Adjunct}/usr/lib \\\n";
1013 
1014                 $Conf32 = "$Tmpdir/$Prog.conf32.$$";
1015                 print CRLE32 "\t-c $Conf32\n";
1016 
1017                 chmod 0755, $Crle32;
1018                 close CRLE32;
1019 
1020                 undef $Conf32 if system($Crle32);
1021 
1022                 # Done with the script
1023                 unlink $Crle32;
1024         }
1025 
1026         # Set $Env so that we will use the config files generated above
1027         # when we run ldd.
1028         if ($Crle64 && $Conf64 && $Crle32 && $Conf32) {
1029                 $Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32";
1030         } elsif ($Crle64 && $Conf64) {
1031                 $Env = "-e LD_FLAGS=config_64=$Conf64";
1032         } elsif ($Crle32 && $Conf32) {
1033                 $Env = "-e LD_FLAGS=config_32=$Conf32";
1034         }
1035 }
1036 
1037 # -----------------------------------------------------------------------------
1038 
1039 # This script relies on ldd returning output reflecting only the binary 
1040 # contents.  But if LD_PRELOAD* environment variables are present, libraries
1041 # named by them will also appear in the output, disrupting our analysis.
1042 # So, before we get too far, scrub the environment.
1043 
1044 delete($ENV{LD_PRELOAD});
1045 delete($ENV{LD_PRELOAD_32});
1046 delete($ENV{LD_PRELOAD_64});
1047 
1048 # Establish a program name for any error diagnostics.
1049 chomp($Prog = `basename $0`);
1050 
1051 # The onbld_elfmod package is maintained in the same directory as this
1052 # script, and is installed in ../lib/perl. Use the local one if present,
1053 # and the installed one otherwise.
1054 my $moddir = dirname($0);
1055 $moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm";
1056 require "$moddir/onbld_elfmod.pm";
1057 
1058 # Determine what machinery is available.
1059 my $Mach = `uname -p`;
1060 my$Isalist = `isalist`;
1061 if ($Mach =~ /sparc/) {
1062         if ($Isalist =~ /sparcv9/) {
1063                 $Ena64 = "ok";
1064         }
1065 } elsif ($Mach =~ /i386/) {
1066         if ($Isalist =~ /amd64/) {
1067                 $Ena64 = "ok";
1068         }
1069 }
1070 
1071 # $Env is used with all calls to ldd. It is set by AltObjectConfig to
1072 # cause an alternate object mapping runtime config file to be used.
1073 $Env = '';
1074 
1075 # Check that we have arguments.
1076 if ((getopts('A:a:D:d:E:e:f:I:imosvw:', \%opt) == 0) ||
1077     (!$opt{f} && ($#ARGV == -1))) {
1078         print "usage: $Prog [-imosv] [-A depfile | -a depdir ] [-D depfile | -d depdir] [-E errfile]\n";
1079         print "\t\t[-e exfile] [-f listfile] [-I infofile] [-w outdir]\n";
1080         print "\t\t[file | dir]...\n";
1081         print "\n";
1082         print "\t[-A depfile]\testablish adjunct dependencies from 'find_elf -r' file list\n";
1083         print "\t[-a depdir]\testablish adjunct dependencies from under directory\n";
1084         print "\t[-D depfile]\testablish dependencies from 'find_elf -r' file list\n";
1085         print "\t[-d depdir]\testablish dependencies from under directory\n";
1086         print "\t[-E errfile]\tdirect error output to file\n";
1087         print "\t[-e exfile]\texceptions file\n";
1088         print "\t[-f listfile]\tuse file list produced by find_elf -r\n";
1089         print "\t[-I infofile]\tdirect informational output (-i, -v) to file\n";
1090         print "\t[-i]\t\tproduce dynamic table entry information\n";
1091         print "\t[-m]\t\tprocess mcs(1) comments\n";
1092         print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
1093         print "\t[-s]\t\tprocess .stab and .symtab entries\n";
1094         print "\t[-v]\t\tprocess version definition entries\n";
1095         print "\t[-w outdir]\tinterpret all files relative to given directory\n";
1096         exit 1;
1097 }
1098 
1099 die "$Prog: -A and -a options are mutually exclusive\n" if ($opt{A} && $opt{a});
1100 die "$Prog: -D and -d options are mutually exclusive\n" if ($opt{D} && $opt{d});
1101 
1102 $Tmpdir = "/tmp" if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir));
1103 
1104 # If -w, change working directory to given location
1105 !$opt{w} || chdir($opt{w}) || die "$Prog: can't cd to $opt{w}";
1106 
1107 # Locate and process the exceptions file
1108 onbld_elfmod::LoadExceptionsToEXRE('check_rtime');
1109 
1110 # Is there a proto area available, either via the -d option, or because
1111 # we are part of an activated workspace?
1112 if ($opt{d}) {
1113         # User specified dependency directory - make sure it exists.
1114         -d $opt{d} || die "$Prog: $opt{d} is not a directory\n";
1115         $Proto = $opt{d};
1116 } elsif ($ENV{CODEMGR_WS}) {
1117         my $Root;
1118 
1119         # Without a user specified dependency directory see if we're
1120         # part of a codemanager workspace and if a proto area exists.
1121         $Proto = $Root if ($Root = $ENV{ROOT}) && (-d $Root);
1122 }
1123 
1124 # Is there an adjunct proto area available, either via the -a option,
1125 # or because we are part of an activated workspace?
1126 if ($opt{a}) {
1127         # User specified dependency directory - make sure it exists.
1128         -d $opt{a} || die "$Prog: $opt{a} is not a directory\n";
1129         $Adjunct = $opt{a};
1130 } elsif ($ENV{CODEMGR_WS}) {
1131         my $Root;
1132 
1133         # Without a user specified dependency directory see if we're
1134         # part of a codemanager workspace and if an adjunct proto area
1135         # exists.
1136         $Adjunct = $Root if ($Root = $ENV{ADJUNCT_PROTO}) && (-d $Root);
1137 }
1138 
1139 # If we are basing this analysis off the sharable objects found in
1140 # a proto area, then gather dependencies and construct an alternative
1141 # dependency mapping via a crle(1) configuration file.
1142 #
1143 # To support alternative dependency mapping we'll need ldd(1)'s
1144 # -e option.  This is relatively new (s81_30), so make sure
1145 # ldd(1) is capable before gathering any dependency information.
1146 if ($opt{D} || $Proto || $Adjunct) {
1147         if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) {
1148                 print "ldd: does not support -e, unable to ";
1149                 print "create alternative dependency mappingings.\n";
1150                 print "ldd: option added under 4390308 (s81_30).\n\n";
1151         } else {
1152                 # If -D or -A was specified, it supplies a list of files in
1153                 # 'find_elf -r' format, and can use it directly. Otherwise,
1154                 # we will run find_elf as a child process to find the
1155                 # sharable objects found under $Proto.
1156                 AltObjectConfig($opt{D} ? $opt{D} : "find_elf -frs $Proto|",
1157                         $opt{A} ? $opt{A} : $Adjunct ? "find_elf -frs $Adjunct|" : "/dev/null");
1158         }
1159 }
1160 
1161 # To support unreferenced dependency detection we'll need ldd(1)'s -U
1162 # option.  This is relatively new (4638070), and if not available we
1163 # can still fall back to -u.  Even with this option, don't use -U with
1164 # releases prior to 5.10 as the cleanup for -U use only got integrated
1165 # into 5.10 under 4642023.  Note, that nightly doesn't typically set a
1166 # RELEASE from the standard <env> files.  Users who wish to disable use
1167 # of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file
1168 # if using nightly, or otherwise establish it in their environment.
1169 if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) {
1170         $LddNoU = 1;
1171 } else {
1172         my($Release);
1173 
1174         if (($Release = $ENV{RELEASE}) && (cmp_os_ver($Release, "<", "5.10"))) {
1175                 $LddNoU = 1;
1176         } else {
1177                 $LddNoU = 0;
1178         }
1179 }
1180 
1181 # Set up variables used to handle output files:
1182 #
1183 # Error messages go to stdout unless -E is specified. $ErrFH is a
1184 # file handle reference that points at the file handle where error messages
1185 # are sent, and $ErrTtl is a reference that points at an integer used
1186 # to count how many lines have been sent there.
1187 #
1188 # Informational messages go to stdout unless -I is specified. $InfoFH is a
1189 # file handle reference that points at the file handle where info messages
1190 # are sent, and $InfoTtl is a reference that points at an integer used
1191 # to count how many lines have been sent there.
1192 #
1193 if ($opt{E}) {
1194         open(ERROR, ">$opt{E}") || die "$Prog: open failed: $opt{E}";
1195         $ErrFH = \*ERROR;
1196 } else {
1197         $ErrFH = \*STDOUT;
1198 }
1199 
1200 if ($opt{I}) {
1201         open(INFO, ">$opt{I}") || die "$Prog: open failed: $opt{I}";
1202         $InfoFH = \*INFO;
1203 } else {
1204         $InfoFH = \*STDOUT;
1205 }
1206 my ($err_dev, $err_ino) = stat($ErrFH);
1207 my ($info_dev, $info_ino) = stat($InfoFH);
1208 $ErrTtl = \$OutCnt1;
1209 $InfoTtl = (($err_dev == $info_dev) && ($err_ino == $info_ino)) ?
1210     \$OutCnt1 : \$OutCnt2;
1211 
1212 
1213 # If we were given a list of objects in 'find_elf -r' format, then
1214 # process it.
1215 ProcFindElf($opt{f}) if $opt{f};
1216 
1217 # Process each argument
1218 foreach my $Arg (@ARGV) {
1219         # Run find_elf to find the files given by $Arg and process them
1220         ProcFindElf("find_elf -fr $Arg|");
1221 }
1222 
1223 # Cleanup output files
1224 unlink $Conf64 if $Conf64;
1225 unlink $Conf32 if $Conf32;
1226 close ERROR if $opt{E};
1227 close INFO if $opt{I};
1228 
1229 exit 0;