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 2009 Sun Microsystems, Inc. All rights reserved. 25# Use is subject to license terms. 26# 27# 28 29# 30# Check ELF information. 31# 32# This script descends a directory hierarchy inspecting ELF dynamic executables 33# and shared objects. The general theme is to verify that common Makefile rules 34# have been used to build these objects. Typical failures occur when Makefile 35# rules are re-invented rather than being inherited from "cmd/lib" Makefiles. 36# 37# As always, a number of components don't follow the rules, and these are 38# excluded to reduce this scripts output. Pathnames used for this exclusion 39# assume this script is being run over a "proto" area. The -a (all) option 40# skips any exclusions. 41# 42# By default any file that has conditions that should be reported is first 43# listed and then each condition follows. The -o (one-line) option produces a 44# more terse output which is better for sorting/diffing with "nightly". 45# 46# NOTE: missing dependencies, symbols or versions are reported by running the 47# file through ldd(1). As objects within a proto area are built to exist in a 48# base system, standard use of ldd(1) will bind any objects to dependencies 49# that exist in the base system. It is frequently the case that newer objects 50# exist in the proto area that are required to satisfy other objects 51# dependencies, and without using these newer objects an ldd(1) will produce 52# misleading error messages. To compensate for this, the -d option (or the 53# existence of the CODEMSG_WS/ROOT environment variables) cause the creation of 54# alternative dependency mappings via crle(1) configuration files that establish 55# any proto shared objects as alternatives to their base system location. Thus 56# ldd(1) can be executed against these configuration files so that objects in a 57# proto area bind to their dependencies in the same proto area. 58 59 60# Define all global variables (required for strict) 61use vars qw($SkipDirs $SkipFiles $SkipTextrelFiles $SkipDirectBindFiles); 62use vars qw($SkipUndefFiles $SkipUnusedDeps); 63use vars qw($SkipStabFiles $SkipNoExStkFiles $SkipCrleConf); 64use vars qw($SkipUnusedSearchPath $SkipUnrefObject $SkipUnusedObject); 65use vars qw($Prog $Mach $Isalist $Env $Ena64 $Tmpdir $Error $Gnuc); 66use vars qw($UnusedPaths $LddNoU $Crle32 $Crle64 $Conf32 $Conf64); 67use vars qw($SkipDirectBindDirs $SkipInterps $SkipSymSort $OldDeps %opt); 68 69use strict; 70 71 72# Define any directories we should skip completely. 73$SkipDirs = qr{ 74 usr/lib/devfsadm | # 4382889 75 usr/lib/libc | # optimized libc 76 usr/lib/rcm | # 4426119 77 usr/perl5 | # alan's taking care of these :-) 78 usr/src # no need to look at shipped source 79}x; 80 81# Define any files we should skip completely. 82$SkipFiles = qr{ ^(?: 83 lddstub | # lddstub has no dependencies 84 geniconvtbl\.so | # 4384329 85 libssagent\.so\.1 | # 4328854 86 libpsvcplugin_psr\.so\.1 | # 4385799 87 libpsvcpolicy_psr\.so\.1 | # " " 88 libpsvcpolicy\.so\.1 | # " " 89 picl_slm\.so | # " " 90 mod_ipp\.so | # Apache loadable module 91 fptest | # USIII specific extns. cause ldd noise on USII bld. m/c 92 grub 93 )$ 94}x; 95 96# Define any files that are allowed text relocations. 97$SkipTextrelFiles = qr{ ^(?: 98 unix | # kernel models are non-pic 99 mdb # relocations against __RTC (dbx) 100 )$ 101}x; 102 103# Define any directories or files that are allowed to have no direct bound 104# symbols 105$SkipDirectBindDirs = qr{ 106 usr/ucb 107}x; 108 109$SkipDirectBindFiles = qr{ ^(?: 110 unix | 111 sbcp | 112 libproc.so.1 | 113 libnisdb.so.2 114 )$ 115}x; 116 117# Define any files that are allowed undefined references. 118 119$SkipUndefFiles = qr{ ^(?: 120 libsvm\.so\.1 | # libspmicommon.so.1 lacking 121 libnisdb\.so\.2 # C++ 122 )$ 123}x; 124 125# Define any files that have unused dependencies. 126$SkipUnusedDeps = qr{ 127 lib/picl/plugins/ | # require devtree dependencies 128 /lib/libp # profile libc makes libm an unused 129}x; # dependency of standard libc 130 131# Define any objects that always look unused. 132$SkipUnusedObject = qr{ 133 /libm_hwcap[0-9]+\.so\.2 # libm.so.2 dependency 134}x; 135 136# Define any files that should contain debugging information. 137$SkipStabFiles = qr{ ^(?: 138 unix 139 )$ 140}x; 141 142# Define any files that don't require a non-executable stack definition. 143$SkipNoExStkFiles = qr{ ^(?: 144 forth | 145 unix | 146 multiboot 147 )$ 148}x; 149 150# Identify any files that should be skipped when building a crle(1) 151# configuration file. As the hwcap libraries can be loop-back mounted onto 152# libc, these can confuse crle(1) because of their identical dev/inode. 153$SkipCrleConf = qr{ 154 lib/libc/libc_hwcap 155}x; 156 157# Skip "unused search path=" ldd(1) diagnostics. 158$SkipUnusedSearchPath = qr{ 159 /usr/lib/fs/autofs.*\ from\ .automountd | # dlopen() 160 /etc/ppp/plugins.*\ from\ .*pppd | # dlopen() 161 /usr/lib/inet/ppp.*\ from\ .*pppd | # dlopen() 162 /usr/sfw/lib.*\ from\ .*libipsecutil.so.1 | # dlopen() 163 /usr/platform/.*rsmlib.*\ from\ .*librsm.so.2 | # dlopen() 164 \$ORIGIN.*\ from\ .*fcode.so | # dlopen() 165 /opt/VRTSvxvm/lib.*\ from\ .*libdiskmgt\.so\.1 | # dlopen() 166 /usr/platform/.*\ from\ .*/usr/platform | # picl 167 /usr/lib/picl/.*\ from\ .*/usr/platform | # picl 168 /usr/platform/.*\ from\ .*/usr/lib/picl | # picl 169 /usr/lib/smbsrv.*\ from\ .*libsmb\.so\.1 | # future needs 170 /usr/lib/mps/secv1.*\ from\ .*libnss3\.so | # non-OSNet 171 /usr/lib/mps.*\ from\ .*libnss3\.so | # non-OSNet 172 /usr/sfw/lib.*\ from\ .*libdbus-1\.so\.3 | # non-OSNet 173 /usr/sfw/lib.*\ from\ .*libdbus-glib-1\.so\.2 | # non-OSNet 174 /usr/sfw/lib.*\ from\ .*libglib-2\.0\.so\.0 | # non-OSNet 175 /usr/X11/lib.*\ from\ .*libglib-2\.0\.so\.0 | # non-OSNet 176 /usr/sfw/lib.*\ from\ .*libgobject-2\.0\.so\.0 | # non-OSNet 177 /usr/X11/lib.*\ from\ .*libgobject-2\.0\.so\.0 | # non-OSNet 178 /usr/sfw/lib.*\ from\ .*libcrypto\.so\.0\.9\.8 | # non-OSNet 179 /usr/sfw/lib.*\ from\ .*libnetsnmp\.so\.5 | # non-OSNet 180 /usr/sfw/lib.*\ from\ .*libgcc_s\.so\.1 | # non-OSNet 181 /usr.*\ from\ .*tst\.gcc\.exe | # gcc built 182 /usr/postgres/8.3/lib.*\ from\ .*libpq\.so\.5 | # non-OSNET 183 /usr/sfw/lib.*\ from\ .*libpq\.so\.5 # non-OSNET 184}x; 185 186# Skip "unreferenced object=" ldd(1) diagnostics. 187$SkipUnrefObject = qr{ 188 /libmapmalloc\.so\.1;\ unused\ dependency\ of | # interposer 189 /libstdc\+\+\.so\.6;\ unused\ dependency\ of | # gcc build 190 /libm\.so\.2.*\ of\ .*libstdc\+\+\.so\.6 | # gcc build 191 /lib.*\ of\ .*/lib/picl/plugins/ | # picl 192 /lib.*\ of\ .*libcimapi\.so | # non-OSNET 193 /lib.*\ of\ .*libjvm\.so | # non-OSNET 194 /lib.*\ of\ .*libnetsnmp\.so\.5 | # non-OSNET 195 /lib.*\ of\ .*libnetsnmpagent\.so\.5 | # non-OSNET 196 /lib.*\ of\ .*libnetsnmpmibs\.so\.5 | # non-OSNET 197 /lib.*\ of\ .*libnetsnmphelpers\.so\.5 | # non-OSNET 198 /lib.*\ of\ .*libnspr4\.so | # non-OSNET 199 /lib.*\ of\ .*libsoftokn3\.so | # non-OSNET 200 /lib.*\ of\ .*libspmicommon\.so\.1 | # non-OSNET 201 /lib.*\ of\ .*libspmocommon\.so\.1 | # non-OSNET 202 /lib.*\ of\ .*libssl3\.so | # non-OSNET 203 /lib.*\ of\ .*libxml2\.so\.2 | # non-OSNET 204 /lib.*\ of\ .*libxslt\.so\.1 | # non-OSNET 205 /lib.*\ of\ .*libpq\.so\.4 # non-OSNET 206}x; 207 208# Define any files that should only have unused (ldd -u) processing. 209$UnusedPaths = qr{ 210 ucb/shutdown # libucb interposes on libc and makes 211 # dependencies on libc seem unnecessary 212}x; 213 214# Define interpreters we should ignore. 215$SkipInterps = qr{ 216 misc/krtld | 217 misc/amd64/krtld | 218 misc/sparcv9/krtld 219}x; 220 221# Catch libintl and libw, although ld(1) will bind to these and thus determine 222# they're needed, their content was moved into libc as of on297 build 7. 223# libthread and libpthread were completely moved into libc as of on10 build 53. 224# libdl was moved into libc as of on10 build 49. librt and libaio were moved 225# into libc as of Nevada build 44. 226$OldDeps = qr{ ^(?: 227 libintl\.so\.1 | 228 libw\.so\.1 | 229 libthread\.so\.1 | 230 libpthread\.so\.1 | 231 libdl\.so\.1 | 232 librt\.so\.1 | 233 libaio\.so\.1 234 )$ 235}x; 236 237# Files for which we skip checking of duplicate addresses in the 238# symbol sort sections. Such exceptions should be rare --- most code will 239# not have duplicate addresses, since it takes assember or a "#pragma weak" 240# to do such aliasing in C. C++ is different: The compiler generates aliases 241# for implementation reasons, and the mangled names used to encode argument 242# and return value types are difficult to handle well in mapfiles. 243# Furthermore, the Sun compiler and gcc use different and incompatible 244# name mangling conventions. Since ON must be buildable by either, we 245# would have to maintain two sets of mapfiles for each such object. 246# C++ use is rare in ON, so this is not worth pursuing. 247# 248$SkipSymSort = qr{ ^.*(?: 249 opt/SUNWdtrt/tst/common/pid/tst.weak2.exe | # DTrace test 250 lib/amd64/libnsl\.so\.1 | # C++ 251 lib/sparcv9/libnsl\.so\.1 | # C++ 252 lib/sparcv9/libfru\.so\.1 | # C++ 253 usr/lib/sgml/nsgmls | # C++ 254 usr/lib/lms | # C++ 255 ld\.so\.1 | # libc_pic.a user 256 lib/libsun_fc\.so\.1 | # C++ 257 lib/amd64/libsun_fc\.so\.1 | # C++ 258 lib/sparcv9/libsun_fc\.so\.1 # C++ 259 )$ 260}x; 261 262use Getopt::Std; 263 264# ----------------------------------------------------------------------------- 265 266# Reliably compare two OS revisions. Arguments are <ver1> <op> <ver2>. 267# <op> is the string form of a normal numeric comparison operator. 268sub cmp_os_ver { 269 my @ver1 = split(/\./, $_[0]); 270 my $op = $_[1]; 271 my @ver2 = split(/\./, $_[2]); 272 273 push @ver2, ("0") x $#ver1 - $#ver2; 274 push @ver1, ("0") x $#ver2 - $#ver1; 275 276 my $diff = 0; 277 while (@ver1 || @ver2) { 278 if (($diff = shift(@ver1) - shift(@ver2)) != 0) { 279 last; 280 } 281 } 282 return (eval "$diff $op 0" ? 1 : 0); 283} 284 285# This script relies on ldd returning output reflecting only the binary 286# contents. But if LD_PRELOAD* environment variables are present, libraries 287# named by them will also appear in the output, disrupting our analysis. 288# So, before we get too far, scrub the environment. 289 290delete($ENV{LD_PRELOAD}); 291delete($ENV{LD_PRELOAD_32}); 292delete($ENV{LD_PRELOAD_64}); 293 294# Establish a program name for any error diagnostics. 295chomp($Prog = `basename $0`); 296 297# Determine what machinery is available. 298$Mach = `uname -p`; 299$Isalist = `isalist`; 300$Env = ""; 301if ($Mach =~ /sparc/) { 302 if ($Isalist =~ /sparcv9/) { 303 $Ena64 = "ok"; 304 } 305} elsif ($Mach =~ /i386/) { 306 if ($Isalist =~ /amd64/) { 307 $Ena64 = "ok"; 308 } 309} 310 311# Check that we have arguments. 312if ((getopts('ad:imosv', \%opt) == 0) || ($#ARGV == -1)) { 313 print "usage: $Prog [-a] [-d depdir] [-m] [-o] [-s] file | dir, ...\n"; 314 print "\t[-a]\t\tprocess all files (ignore any exception lists)\n"; 315 print "\t[-d dir]\testablish dependencies from under directory\n"; 316 print "\t[-i]\t\tproduce dynamic table entry information\n"; 317 print "\t[-m]\t\tprocess mcs(1) comments\n"; 318 print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n"; 319 print "\t[-s]\t\tprocess .stab and .symtab entries\n"; 320 print "\t[-v]\t\tprocess version definition entries\n"; 321 exit 1; 322} else { 323 my($Proto); 324 325 if ($opt{d}) { 326 # User specified dependency directory - make sure it exists. 327 if (! -d $opt{d}) { 328 print "$Prog: $opt{d} is not a directory\n"; 329 exit 1; 330 } 331 $Proto = $opt{d}; 332 333 } elsif ($ENV{CODEMGR_WS}) { 334 my($Root); 335 336 # Without a user specified dependency directory see if we're 337 # part of a codemanager workspace and if a proto area exists. 338 if (($Root = $ENV{ROOT}) && (-d $Root)) { 339 $Proto = $Root; 340 } 341 } 342 343 if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir)) { 344 $Tmpdir = "/tmp"; 345 } 346 347 # Determine whether this is a __GNUC build. If so, unused search path 348 # processing is disabled. 349 if (defined $ENV{__GNUC}) { 350 $Gnuc = 1; 351 } else { 352 $Gnuc = 0; 353 } 354 355 # Look for dependencies under $Proto. 356 if ($Proto) { 357 # To support alternative dependency mapping we'll need ldd(1)'s 358 # -e option. This is relatively new (s81_30), so make sure 359 # ldd(1) is capable before gathering any dependency information. 360 if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) { 361 print "ldd: does not support -e, unable to "; 362 print "create alternative dependency mappingings.\n"; 363 print "ldd: option added under 4390308 (s81_30).\n\n"; 364 } else { 365 # Gather dependencies and construct a alternative 366 # dependency mapping via a crle(1) configuration file. 367 GetDeps($Proto, "/"); 368 GenConf(); 369 } 370 } 371 372 # To support unreferenced dependency detection we'll need ldd(1)'s -U 373 # option. This is relatively new (4638070), and if not available we 374 # can still fall back to -u. Even with this option, don't use -U with 375 # releases prior to 5.10 as the cleanup for -U use only got integrated 376 # into 5.10 under 4642023. Note, that nightly doesn't typically set a 377 # RELEASE from the standard <env> files. Users who wish to disable use 378 # of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file 379 # if using nightly, or otherwise establish it in their environment. 380 if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) { 381 $LddNoU = 1; 382 } else { 383 my($Release); 384 385 if (($Release = $ENV{RELEASE}) && 386 (cmp_os_ver($Release, "<", "5.10"))) { 387 $LddNoU = 1; 388 } else { 389 $LddNoU = 0; 390 } 391 } 392 393 # For each argument determine if we're dealing with a file or directory. 394 foreach my $Arg (@ARGV) { 395 # Ignore symbolic links. 396 if (-l $Arg) { 397 next; 398 } 399 400 if (!stat($Arg)) { 401 next; 402 } 403 404 # Process simple files. 405 if (-f _) { 406 my($RelPath) = $Arg; 407 my($File) = $Arg; 408 my($Secure) = 0; 409 410 $RelPath =~ s!^.*/!./!; 411 $File =~ s!^.*/!!; 412 413 if (-u _ || -g _) { 414 $Secure = 1; 415 } 416 417 ProcFile($Arg, $RelPath, $File, $Secure); 418 next; 419 } 420 # Process directories. 421 if (-d _) { 422 ProcDir($Arg, "."); 423 next; 424 } 425 426 print "$Arg is not a file or directory\n"; 427 $Error = 1; 428 } 429 430 # Cleanup 431 CleanUp(); 432} 433 434$Error = 0; 435 436# Clean up any temporary files. 437sub CleanUp { 438 if ($Crle64) { 439 unlink $Crle64; 440 } 441 if ($Conf64) { 442 unlink $Conf64; 443 } 444 if ($Crle32) { 445 unlink $Crle32; 446 } 447 if ($Conf32) { 448 unlink $Conf32; 449 } 450} 451 452# Create an output message, either a one-liner (under -o) or preceded by the 453# files relative pathname as a title. 454sub OutMsg { 455 my($Ttl, $Path, $Msg) = @_; 456 457 if ($opt{o}) { 458 $Msg =~ s/^[ \t]*//; 459 print "$Path: $Msg\n"; 460 } else { 461 if ($Ttl eq 0) { 462 print "==== $Path ====\n"; 463 } 464 print "$Msg\n"; 465 } 466} 467 468# Determine whether this a ELF dynamic object and if so investigate its runtime 469# attributes. 470sub ProcFile { 471 my($FullPath, $RelPath, $File, $Secure) = @_; 472 my(@Elf, @Ldd, $Dyn, $Intp, $Dll, $Ttl, $Sym, $Interp, $Stack); 473 my($Sun, $Relsz, $Pltsz, $Tex, $Stab, $Strip, $Lddopt, $SymSort); 474 my($Val, $Header, $SkipLdd, $IsX86, $RWX, $UnDep); 475 my($HasDirectBinding, $HasVerdef); 476 477 # Ignore symbolic links. 478 if (-l $FullPath) { 479 return; 480 } 481 482 $Ttl = 0; 483 @Ldd = 0; 484 485 # Determine whether we have access to inspect the file. 486 if (!(-r $FullPath)) { 487 OutMsg($Ttl++, $RelPath, 488 "\tunable to inspect file: permission denied"); 489 return; 490 } 491 492 # Determine if this is a file we don't care about. 493 if (!$opt{a}) { 494 if ($File =~ $SkipFiles) { 495 return; 496 } 497 } 498 499 # Determine whether we have a executable (static or dynamic) or a 500 # shared object. 501 @Elf = split(/\n/, `elfdump -epdicyv $FullPath 2>&1`); 502 503 $Dyn = $Intp = $Dll = $Stack = $IsX86 = $RWX = 0; 504 $Interp = 1; 505 $Header = 'None'; 506 foreach my $Line (@Elf) { 507 # If we have an invalid file type (which we can tell from the 508 # first line), or we're processing an archive, bail. 509 if ($Header eq 'None') { 510 if (($Line =~ /invalid file/) || 511 ($Line =~ /$FullPath(.*):/)) { 512 return; 513 } 514 } 515 516 if ($Line =~ /^ELF Header/) { 517 $Header = 'Ehdr'; 518 519 } elsif ($Line =~ /^Program Header/) { 520 $Header = 'Phdr'; 521 $RWX = 0; 522 523 } elsif ($Line =~ /^Interpreter/) { 524 $Header = 'Intp'; 525 526 } elsif ($Line =~ /^Dynamic Section/) { 527 # A dynamic section indicates we're a dynamic object 528 # (this makes sure we don't check static executables). 529 $Dyn = 1; 530 531 } elsif (($Header eq 'Ehdr') && ($Line =~ /e_type:/)) { 532 # The e_type field indicates whether this file is a 533 # shared object (ET_DYN) or an executable (ET_EXEC). 534 if ($Line =~ /ET_DYN/) { 535 $Dll = 1; 536 } elsif ($Line !~ /ET_EXEC/) { 537 return; 538 } 539 } elsif (($Header eq 'Ehdr') && ($Line =~ /ei_class:/)) { 540 # If we encounter a 64-bit object, but we're not running 541 # on a 64-bit system, suppress calling ldd(1). 542 if (($Line =~ /ELFCLASS64/) && !$Ena64) { 543 $SkipLdd = 1; 544 } 545 } elsif (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) { 546 # If it's a X86 object, we need to enforce RW- data. 547 if (($Line =~ /(EM_AMD64|EM_386)/)) { 548 $IsX86 = 1; 549 } 550 } elsif (($Header eq 'Phdr') && 551 ($Line =~ /\[ PF_X PF_W PF_R \]/)) { 552 # RWX segment seen. 553 $RWX = 1; 554 555 } elsif (($Header eq 'Phdr') && 556 ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) { 557 # Seen an RWX PT_LOAD segment. 558 if ($File !~ $SkipNoExStkFiles) { 559 OutMsg($Ttl++, $RelPath, 560 "\tapplication requires non-executable " . 561 "data\t<no -Mmapfile_noexdata?>"); 562 } 563 564 } elsif (($Header eq 'Phdr') && 565 ($Line =~ /\[ PT_SUNWSTACK \]/)) { 566 # This object defines a non-executable stack. 567 $Stack = 1; 568 569 } elsif (($Header eq 'Intp') && !$opt{a} && 570 ($Line =~ $SkipInterps)) { 571 # This object defines an interpretor we should skip. 572 $Interp = 0; 573 } 574 } 575 576 # Determine whether this ELF executable or shared object has a 577 # conforming mcs(1) comment section. If the correct $(POST_PROCESS) 578 # macros are used, only a 3 or 4 line .comment section should exist 579 # containing one or two "@(#)SunOS" identifying comments (one comment 580 # for a non-debug build, and two for a debug build). The results of 581 # the following split should be three or four lines, the last empty 582 # line being discarded by the split. 583 if ($opt{m}) { 584 my(@Mcs, $Con, $Dev); 585 586 @Mcs = split(/\n/, `mcs -p $FullPath 2>&1`); 587 588 $Con = $Dev = $Val = 0; 589 foreach my $Line (@Mcs) { 590 $Val++; 591 592 if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) { 593 $Con = 1; 594 last; 595 } 596 if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) { 597 $Dev = 1; 598 next; 599 } 600 if (($Dev == 0) && ($Val == 4)) { 601 $Con = 1; 602 last; 603 } 604 if (($Dev == 1) && ($Val == 5)) { 605 $Con = 1; 606 last; 607 } 608 } 609 if ($opt{m} && ($Con == 1)) { 610 OutMsg($Ttl++, $RelPath, 611 "\tnon-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>"); 612 } 613 } 614 615 # Applications should contain a non-executable stack definition. 616 if (($Dll == 0) && ($Stack == 0)) { 617 if (!$opt{a}) { 618 if ($File =~ $SkipNoExStkFiles) { 619 goto DYN; 620 } 621 } 622 OutMsg($Ttl++, $RelPath, 623 "\tapplication requires non-executable stack\t<no -Mmapfile_noexstk?>"); 624 } 625 626DYN: 627 # Having caught any static executables in the mcs(1) check and non- 628 # executable stack definition check, continue with dynamic objects 629 # from now on. 630 if ($Dyn eq 0) { 631 return; 632 } 633 634 # Only use ldd unless we've encountered an interpreter that should 635 # be skipped. 636 if (!$SkipLdd && $Interp) { 637 my $LDDFullPath = $FullPath; 638 639 if ($Secure) { 640 # The execution of a secure application over an nfs file 641 # system mounted nosuid will result in warning messages 642 # being sent to /var/adm/messages. As this type of 643 # environment can occur with root builds, move the file 644 # being investigated to a safe place first. In addition 645 # remove its secure permission so that it can be 646 # influenced by any alternative dependency mappings. 647 648 my($TmpPath) = "$Tmpdir/$File"; 649 650 system('cp', $LDDFullPath, $TmpPath); 651 chmod 0777, $TmpPath; 652 $LDDFullPath = $TmpPath; 653 } 654 655 # Use ldd(1) to determine the objects relocatability and use. 656 # By default look for all unreferenced dependencies. However, 657 # some objects have legitimate dependencies that they do not 658 # reference. 659 if ($LddNoU || ($RelPath =~ $UnusedPaths)) { 660 $Lddopt = "-ru"; 661 } else { 662 $Lddopt = "-rU"; 663 } 664 @Ldd = split(/\n/, `ldd $Lddopt $Env $LDDFullPath 2>&1`); 665 if ($Secure) { 666 unlink $LDDFullPath; 667 } 668 } 669 670 $Val = 0; 671 $Sym = 5; 672 $UnDep = 1; 673 674 foreach my $Line (@Ldd) { 675 676 if ($Val == 0) { 677 $Val = 1; 678 # Make sure ldd(1) worked. One possible failure is that 679 # this is an old ldd(1) prior to -e addition (4390308). 680 if ($Line =~ /usage:/) { 681 $Line =~ s/$/\t<old ldd(1)?>/; 682 OutMsg($Ttl++, $RelPath, $Line); 683 last; 684 } elsif ($Line =~ /execution failed/) { 685 OutMsg($Ttl++, $RelPath, $Line); 686 last; 687 } 688 689 # It's possible this binary can't be executed, ie. we've 690 # found a sparc binary while running on an intel system, 691 # or a sparcv9 binary on a sparcv7/8 system. 692 if ($Line =~ /wrong class/) { 693 OutMsg($Ttl++, $RelPath, 694 "\thas wrong class or data encoding"); 695 next; 696 } 697 698 # Historically, ldd(1) likes executable objects to have 699 # their execute bit set. Note that this test isn't 700 # applied unless the -a option is in effect, as any 701 # non-executable files are skipped by default to reduce 702 # the cost of running this script. 703 if ($Line =~ /not executable/) { 704 OutMsg($Ttl++, $RelPath, 705 "\tis not executable"); 706 next; 707 } 708 } 709 710 # Look for "file" or "versions" that aren't found. Note that 711 # these lines will occur before we find any symbol referencing 712 # errors. 713 if (($Sym == 5) && ($Line =~ /not found\)/)) { 714 if ($Line =~ /file not found\)/) { 715 $Line =~ s/$/\t<no -zdefs?>/; 716 } 717 OutMsg($Ttl++, $RelPath, $Line); 718 next; 719 } 720 # Look for relocations whose symbols can't be found. Note, we 721 # only print out the first 5 relocations for any file as this 722 # output can be excessive. 723 if ($Sym && ($Line =~ /symbol not found/)) { 724 # Determine if this file is allowed undefined 725 # references. 726 if ($Sym == 5) { 727 if (!$opt{a}) { 728 if ($File =~ $SkipUndefFiles) { 729 $Sym = 0; 730 next; 731 } 732 } 733 } 734 if ($Sym-- == 1) { 735 if (!$opt{o}) { 736 OutMsg($Ttl++, $RelPath, 737 "\tcontinued ..."); 738 } 739 next; 740 } 741 # Just print the symbol name. 742 $Line =~ s/$/\t<no -zdefs?>/; 743 OutMsg($Ttl++, $RelPath, $Line); 744 next; 745 } 746 # Look for any unused search paths. 747 if ($Line =~ /unused search path=/) { 748 # Note, skip this comparison for __GNUC builds, as the 749 # gnu compilers insert numerous unused search paths. 750 if ($Gnuc == 1) { 751 next; 752 } 753 if (!$opt{a}) { 754 if ($Line =~ $SkipUnusedSearchPath) { 755 next; 756 } 757 } 758 if ($Secure) { 759 $Line =~ s!$Tmpdir/!!; 760 } 761 $Line =~ s/^[ \t]*(.*)/\t$1\t<remove search path?>/; 762 OutMsg($Ttl++, $RelPath, $Line); 763 next; 764 } 765 # Look for unreferenced dependencies. Note, if any unreferenced 766 # objects are ignored, then set $UnDep so as to suppress any 767 # associated unused-object messages. 768 if ($Line =~ /unreferenced object=/) { 769 if (!$opt{a}) { 770 if ($Line =~ $SkipUnrefObject) { 771 $UnDep = 0; 772 next; 773 } 774 } 775 if ($Secure) { 776 $Line =~ s!$Tmpdir/!!; 777 } 778 $Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/; 779 OutMsg($Ttl++, $RelPath, $Line); 780 next; 781 } 782 # Look for any unused dependencies. 783 if ($UnDep && ($Line =~ /unused/)) { 784 if (!$opt{a}) { 785 if ($RelPath =~ $SkipUnusedDeps) { 786 next; 787 } 788 if ($Line =~ $SkipUnusedObject) { 789 next; 790 } 791 } 792 if ($Secure) { 793 $Line =~ s!$Tmpdir/!!; 794 } 795 $Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/; 796 OutMsg($Ttl++, $RelPath, $Line); 797 next; 798 } 799 } 800 801 # Reuse the elfdump(1) data to investigate additional dynamic linking 802 # information. 803 804 $Sun = $Relsz = $Pltsz = $Dyn = $Stab = $SymSort = 0; 805 $Tex = $Strip = 1; 806 $HasDirectBinding = 0; 807 $HasVerdef = 0; 808 809 $Header = 'None'; 810ELF: foreach my $Line (@Elf) { 811 # We're only interested in the section headers and the dynamic 812 # section. 813 if ($Line =~ /^Section Header/) { 814 $Header = 'Shdr'; 815 816 if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) { 817 # This object has a combined relocation section. 818 $Sun = 1; 819 820 } elsif (($Stab == 0) && ($Line =~ /\.stab/)) { 821 # This object contain .stabs sections 822 $Stab = 1; 823 } elsif (($SymSort == 0) && 824 ($Line =~ /\.SUNW_dyn(sym)|(tls)sort/)) { 825 # This object contains a symbol sort section 826 $SymSort = 1; 827 } 828 829 if (($Strip == 1) && ($Line =~ /\.symtab/)) { 830 # This object contains a complete symbol table. 831 $Strip = 0; 832 } 833 next; 834 835 } elsif ($Line =~ /^Dynamic Section/) { 836 $Header = 'Dyn'; 837 next; 838 } elsif ($Line =~ /^Syminfo Section/) { 839 $Header = 'Syminfo'; 840 next; 841 } elsif ($Line =~ /^Version Definition Section/) { 842 $HasVerdef = 1; 843 next; 844 } elsif (($Header ne 'Dyn') && ($Header ne 'Syminfo')) { 845 next; 846 } 847 848 # Look into the Syminfo section. 849 # Does this object have at least one Directly Bound symbol? 850 if (($Header eq 'Syminfo')) { 851 my(@Symword); 852 853 if ($HasDirectBinding == 1) { 854 next; 855 } 856 857 @Symword = split(' ', $Line); 858 859 if (!defined($Symword[1])) { 860 next; 861 } 862 if ($Symword[1] =~ /B/) { 863 $HasDirectBinding = 1; 864 } 865 next; 866 } 867 868 # Does this object contain text relocations. 869 if ($Tex && ($Line =~ /TEXTREL/)) { 870 # Determine if this file is allowed text relocations. 871 if (!$opt{a}) { 872 if ($File =~ $SkipTextrelFiles) { 873 $Tex = 0; 874 next ELF; 875 } 876 } 877 OutMsg($Ttl++, $RelPath, 878 "\tTEXTREL .dynamic tag\t\t\t<no -Kpic?>"); 879 $Tex = 0; 880 next; 881 } 882 883 # Does this file have any relocation sections (there are a few 884 # psr libraries with no relocations at all, thus a .SUNW_reloc 885 # section won't exist either). 886 if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) { 887 $Relsz = hex((split(' ', $Line))[2]); 888 next; 889 } 890 891 # Does this file have any plt relocations. If the plt size is 892 # equivalent to the total relocation size then we don't have 893 # any relocations suitable for combining into a .SUNW_reloc 894 # section. 895 if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) { 896 $Pltsz = hex((split(' ', $Line))[2]); 897 next; 898 } 899 900 # Does this object have any dependencies. 901 if ($Line =~ /NEEDED/) { 902 my($Need) = (split(' ', $Line))[3]; 903 904 if ($Need =~ $OldDeps) { 905 # Catch any old (unnecessary) dependencies. 906 OutMsg($Ttl++, $RelPath, 907 "\tNEEDED=$Need\t<dependency no longer necessary>"); 908 } elsif ($opt{i}) { 909 # Under the -i (information) option print out 910 # any useful dynamic entries. 911 OutMsg($Ttl++, $RelPath, "\tNEEDED=$Need"); 912 } 913 next; 914 } 915 916 # Is this object built with -B direct flag on? 917 if ($Line =~ / DIRECT /) { 918 $HasDirectBinding = 1; 919 } 920 921 # Does this object specify a runpath. 922 if ($opt{i} && ($Line =~ /RPATH/)) { 923 my($Rpath) = (split(' ', $Line))[3]; 924 OutMsg($Ttl++, $RelPath, "\tRPATH=$Rpath"); 925 next; 926 } 927 } 928 929 # A shared object, that contains non-plt relocations, should have a 930 # combined relocation section indicating it was built with -z combreloc. 931 if ($Dll && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) { 932 OutMsg($Ttl++, $RelPath, 933 "\tSUNW_reloc section missing\t\t<no -zcombreloc?>"); 934 } 935 936 # No objects released to a customer should have any .stabs sections 937 # remaining, they should be stripped. 938 if ($opt{s} && $Stab) { 939 if (!$opt{a}) { 940 if ($File =~ $SkipStabFiles) { 941 goto DONESTAB; 942 } 943 } 944 OutMsg($Ttl++, $RelPath, 945 "\tdebugging sections should be deleted\t<no strip -x?>"); 946 } 947 948 # Identify an object that is not built with either -B direct or 949 # -z direct. 950 if (($RelPath =~ $SkipDirectBindDirs) || 951 ($File =~ $SkipDirectBindFiles)) { 952 goto DONESTAB; 953 } 954 if ($Relsz && ($HasDirectBinding == 0)) { 955 OutMsg($Ttl++, $RelPath, 956 "\tobject has no direct bindings\t<no -B direct or -z direct?>"); 957 } 958 959DONESTAB: 960 961 # All objects should have a full symbol table to provide complete 962 # debugging stack traces. 963 if ($Strip) { 964 OutMsg($Ttl++, $RelPath, 965 "\tsymbol table should not be stripped\t<remove -s?>"); 966 } 967 968 # If there are symbol sort sections in this object, report on 969 # any that have duplicate addresses. 970 ProcSymSort($FullPath, $RelPath, \$Ttl) if $SymSort; 971 972 # If -v was specified, and the object has a version definition 973 # section, generate output showing each public symbol and the 974 # version it belongs to. 975 ProcVerdef($FullPath, $RelPath, \$Ttl) if $HasVerdef && $opt{v}; 976} 977 978 979## ProcSymSortOutMsg(RefTtl, RelPath, secname, addr, names...) 980# 981# Call OutMsg for a duplicate address error in a symbol sort 982# section 983# 984sub ProcSymSortOutMsg { 985 my($RefTtl, $RelPath, $secname, $addr, @names) = @_; 986 987 OutMsg($$RefTtl++, $RelPath, 988 "$secname: duplicate $addr: ". join(', ', @names)); 989} 990 991 992## ProcSymSort(FullPath, RelPath) 993# 994# Examine the symbol sort sections for the given object and report 995# on any duplicate addresses found. Ideally, mapfile directives 996# should be used when building objects that have multiple symbols 997# with the same address so that only one of them appears in the sort 998# section. This saves space, reduces user confusion, and ensures that 999# libproc and debuggers always display public names instead of symbols 1000# that are merely implementation details. 1001# 1002sub ProcSymSort { 1003 1004 my($FullPath, $RelPath, $RefTtl) = @_; 1005 1006 # If this object is exempt from checking, return quietly 1007 return if ($FullPath =~ $SkipSymSort); 1008 1009 1010 open(SORT, "elfdump -S $FullPath|") || 1011 die "$Prog: Unable to execute elfdump (symbol sort sections)\n"; 1012 1013 my $line; 1014 my $last_addr; 1015 my @dups = (); 1016 my $secname; 1017 while ($line = <SORT>) { 1018 chomp $line; 1019 1020 next if ($line eq ''); 1021 1022 # If this is a header line, pick up the section name 1023 if ($line =~ /^Symbol Sort Section:\s+([^\s]+)\s+/) { 1024 $secname = $1; 1025 1026 # Every new section is followed by a column header line 1027 $line = <SORT>; # Toss header line 1028 1029 # Flush anything left from previous section 1030 ProcSymSortOutMsg($RefTtl, $RelPath, $secname, 1031 $last_addr, @dups) if (scalar(@dups) > 1); 1032 1033 # Reset variables for new sort section 1034 $last_addr = ''; 1035 @dups = (); 1036 1037 next; 1038 } 1039 1040 # Process symbol line 1041 my @fields = split /\s+/, $line; 1042 my $new_addr = $fields[2]; 1043 my $new_type = $fields[8]; 1044 my $new_name = $fields[9]; 1045 1046 if ($new_type eq 'UNDEF') { 1047 OutMsg($RefTtl++, $RelPath, 1048 "$secname: unexpected UNDEF symbol " . 1049 "(link-editor error): $new_name"); 1050 next; 1051 } 1052 1053 if ($new_addr eq $last_addr) { 1054 push @dups, $new_name; 1055 } else { 1056 ProcSymSortOutMsg($RefTtl, $RelPath, $secname, 1057 $last_addr, @dups) if (scalar(@dups) > 1); 1058 @dups = ( $new_name ); 1059 $last_addr = $new_addr; 1060 } 1061 } 1062 1063 ProcSymSortOutMsg($RefTtl, $RelPath, $secname, $last_addr, @dups) 1064 if (scalar(@dups) > 1); 1065 1066 close SORT; 1067} 1068 1069 1070## ProcVerdef(FullPath, RelPath) 1071# 1072# Examine the version definition section for the given object and report 1073# each public symbol along with the version it belongs to. 1074# 1075sub ProcVerdef { 1076 1077 my($FullPath, $RelPath, $RefTtl) = @_; 1078 my $line; 1079 my $cur_ver = ''; 1080 my $tab = $opt{o} ? '' : "\t"; 1081 1082 # pvs -dov provides information about the versioning hierarchy 1083 # in the file. Lines are of the format: 1084 # path - version[XXX]; 1085 # where [XXX] indicates optional information, such as flags 1086 # or inherited versions. 1087 # 1088 # Private versions are allowed to change freely, so ignore them. 1089 open(PVS, "pvs -dov $FullPath|") || 1090 die "$Prog: Unable to execute pvs (version definition section)\n"; 1091 1092 while ($line = <PVS>) { 1093 chomp $line; 1094 1095 if ($line =~ /^[^\s]+\s+-\s+([^;]+)/) { 1096 my $ver = $1; 1097 1098 next if $ver =~ /private/i; 1099 OutMsg($$RefTtl++, $RelPath, "${tab}VERDEF=$ver"); 1100 } 1101 } 1102 close PVS; 1103 1104 # pvs -dos lists the symbols assigned to each version definition. 1105 # Lines are of the format: 1106 # path - version: symbol; 1107 # path - version: symbol (size); 1108 # where the (size) is added to data items, but not for functions. 1109 # We strip off the size, if present. 1110 1111 open(PVS, "pvs -dos $FullPath|") || 1112 die "$Prog: Unable to execute pvs (version definition section)\n"; 1113 while ($line = <PVS>) { 1114 chomp $line; 1115 if ($line =~ /^[^\s]+\s+-\s+([^:]+):\s*([^\s;]+)/) { 1116 my $ver = $1; 1117 my $sym = $2; 1118 1119 next if $ver =~ /private/i; 1120 1121 if ($opt{o}) { 1122 OutMsg($$RefTtl++, $RelPath, 1123 "VERSION=$ver, SYMBOL=$sym"); 1124 } else { 1125 if ($cur_ver ne $ver) { 1126 OutMsg($$RefTtl++, $RelPath, "\tVERSION=$ver"); 1127 $cur_ver = $ver; 1128 } 1129 OutMsg($$RefTtl++, $RelPath, "\t\tSYMBOL=$sym"); 1130 } 1131 } 1132 } 1133 1134 close PVS; 1135} 1136 1137 1138sub ProcDir { 1139 my($FullDir, $RelDir) = @_; 1140 my($NewFull, $NewRel); 1141 1142 # Determine if this is a directory we don't care about. 1143 if (!$opt{a}) { 1144 if ($RelDir =~ $SkipDirs) { 1145 return; 1146 } 1147 } 1148 1149 # Open the directory and read each entry, omit files starting with "." 1150 if (opendir(DIR, $FullDir)) { 1151 foreach my $Entry (readdir(DIR)) { 1152 if ($Entry =~ /^\./) { 1153 next; 1154 } 1155 $NewFull = "$FullDir/$Entry"; 1156 1157 # Ignore symlinks. 1158 if (-l $NewFull) { 1159 next; 1160 } 1161 if (!stat($NewFull)) { 1162 next; 1163 } 1164 $NewRel = "$RelDir/$Entry"; 1165 1166 # Descend into and process any directories. 1167 if (-d _) { 1168 ProcDir($NewFull, $NewRel); 1169 next; 1170 } 1171 1172 # Typically dynamic objects are executable, so we can 1173 # reduce the overall cost of this script (a lot!) by 1174 # screening out non-executables here, rather than pass 1175 # them to file(1) later. However, it has been known 1176 # for shared objects to be mistakenly left non- 1177 # executable, so with -a let all files through so that 1178 # this requirement can be verified (see ProcFile()). 1179 if (!$opt{a}) { 1180 if (! -x _) { 1181 next; 1182 } 1183 } 1184 1185 # Process any standard files. 1186 if (-f _) { 1187 my($Secure) = 0; 1188 1189 if (-u _ || -g _) { 1190 $Secure = 1; 1191 } 1192 1193 ProcFile($NewFull, $NewRel, $Entry, $Secure); 1194 next; 1195 } 1196 1197 } 1198 closedir(DIR); 1199 } 1200} 1201 1202# Create a crle(1) script for any 64-bit dependencies we locate. A runtime 1203# configuration file will be generated to establish alternative dependency 1204# mappings for all these dependencies. 1205 1206sub Entercrle64 { 1207 my($FullDir, $RelDir, $Entry) = @_; 1208 1209 if (!$Crle64) { 1210 # Create and initialize the script if is doesn't already exit. 1211 1212 $Crle64 = "$Tmpdir/$Prog.crle64.$$"; 1213 open(CRLE64, "> $Crle64") || 1214 die "$Prog: open failed: $Crle64: $!"; 1215 1216 print CRLE64 "#!/bin/sh\ncrle -64\\\n"; 1217 } 1218 print CRLE64 "\t-o $FullDir -a $RelDir/$Entry \\\n"; 1219} 1220 1221# Create a crle(1) script for any 32-bit dependencies we locate. A runtime 1222# configuration file will be generated to establish alternative dependency 1223# mappings for all these dependencies. 1224 1225sub Entercrle32 { 1226 my($FullDir, $RelDir, $Entry) = @_; 1227 1228 if (!$Crle32) { 1229 # Create and initialize the script if is doesn't already exit. 1230 1231 $Crle32 = "$Tmpdir/$Prog.crle32.$$"; 1232 open(CRLE32, "> $Crle32") || 1233 die "$Prog: open failed: $Crle32: $!"; 1234 1235 print CRLE32 "#!/bin/sh\ncrle \\\n"; 1236 } 1237 print CRLE32 "\t-o $FullDir -a $RelDir/$Entry \\\n"; 1238} 1239 1240# Having finished gathering dependencies, complete any crle(1) scripts and 1241# execute them to generate the associated runtime configuration files. In 1242# addition establish the environment variable required to pass the configuration 1243# files to ldd(1). 1244 1245sub GenConf { 1246 if ($Crle64) { 1247 $Conf64 = "$Tmpdir/$Prog.conf64.$$"; 1248 print CRLE64 "\t-c $Conf64\n"; 1249 1250 chmod 0755, $Crle64; 1251 close CRLE64; 1252 1253 if (system($Crle64)) { 1254 undef $Conf64; 1255 } 1256 } 1257 if ($Crle32) { 1258 $Conf32 = "$Tmpdir/$Prog.conf32.$$"; 1259 print CRLE32 "\t-c $Conf32\n"; 1260 1261 chmod 0755, $Crle32; 1262 close CRLE32; 1263 1264 if (system($Crle32)) { 1265 undef $Conf32; 1266 } 1267 } 1268 1269 if ($Crle64 && $Conf64 && $Crle32 && $Conf32) { 1270 $Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32"; 1271 } elsif ($Crle64 && $Conf64) { 1272 $Env = "-e LD_FLAGS=config_64=$Conf64"; 1273 } elsif ($Crle32 && $Conf32) { 1274 $Env = "-e LD_FLAGS=config_32=$Conf32"; 1275 } 1276} 1277 1278# Recurse through a directory hierarchy looking for appropriate dependencies. 1279 1280sub GetDeps { 1281 my($FullDir, $RelDir) = @_; 1282 my($NewFull); 1283 1284 # Open the directory and read each entry, omit files starting with "." 1285 if (opendir(DIR, $FullDir)) { 1286 foreach my $Entry (readdir(DIR)) { 1287 if ($Entry =~ /^\./) { 1288 next; 1289 } 1290 $NewFull = "$FullDir/$Entry"; 1291 1292 # We need to follow links so that any dependencies 1293 # are expressed in all their available forms. 1294 # Bail on symlinks like 32 -> . 1295 if (-l $NewFull) { 1296 if (readlink($NewFull) =~ /^\.$/) { 1297 next; 1298 } 1299 } 1300 if (!stat($NewFull)) { 1301 next; 1302 } 1303 1304 if (!$opt{a}) { 1305 if ($NewFull =~ $SkipCrleConf) { 1306 next; 1307 } 1308 } 1309 1310 # If this is a directory descend into it. 1311 if (-d _) { 1312 my($NewRel); 1313 1314 if ($RelDir =~ /^\/$/) { 1315 $NewRel = "$RelDir$Entry"; 1316 } else { 1317 $NewRel = "$RelDir/$Entry"; 1318 } 1319 1320 GetDeps($NewFull, $NewRel); 1321 next; 1322 } 1323 1324 # If this is a regular file determine if its a 1325 # valid ELF dependency. 1326 if (-f _) { 1327 my($File); 1328 1329 # Typically shared object dependencies end with 1330 # ".so" or ".so.?", hence we can reduce the cost 1331 # of this script (a lot!) by screening out files 1332 # that don't follow this pattern. 1333 if (!$opt{a}) { 1334 if ($Entry !~ /\.so(?:\.\d+)*$/) { 1335 next; 1336 } 1337 } 1338 1339 $File = `file $NewFull`; 1340 if ($File !~ /dynamic lib/) { 1341 next; 1342 } 1343 1344 if ($File =~ /32-bit/) { 1345 Entercrle32($FullDir, $RelDir, $Entry); 1346 } elsif ($Ena64) { 1347 Entercrle64($FullDir, $RelDir, $Entry); 1348 } 1349 next; 1350 } 1351 } 1352 closedir(DIR); 1353 } 1354} 1355exit $Error 1356