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