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