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, Version 1.0 only 7# (the "License"). You may not use this file except in compliance 8# with the License. 9# 10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11# or http://www.opensolaris.org/os/licensing. 12# See the License for the specific language governing permissions 13# and limitations under the License. 14# 15# When distributing Covered Code, include this CDDL HEADER in each 16# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17# If applicable, add the following below this CDDL HEADER, with the 18# fields enclosed by brackets "[]" replaced with your own identifying 19# information: Portions Copyright [yyyy] [name of copyright owner] 20# 21# CDDL HEADER END 22# 23# 24# Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25# Use is subject to license terms. 26# 27# ident "%Z%%M% %I% %E% SMI" 28# 29# Check ELF information. 30# 31# This script descends a directory hierarchy inspecting ELF dynamic executables 32# and shared objects. The general theme is to verify that common Makefile rules 33# have been used to build these objects. Typical failures occur when Makefile 34# rules are re-invented rather than being inherited from "cmd/lib" Makefiles. 35# 36# As always, a number of components don't follow the rules, and these are 37# excluded to reduce this scripts output. Pathnames used for this exclusion 38# assume this script is being run over a "proto" area. The -a (all) option 39# skips any exclusions. 40# 41# By default any file that has conditions that should be reported is first 42# listed and then each condition follows. The -o (one-line) option produces a 43# more terse output which is better for sorting/diffing with "nightly". 44# 45# NOTE: missing dependencies, symbols or versions are reported by running the 46# file through ldd(1). As objects within a proto area are built to exist in a 47# base system, standard use of ldd(1) will bind any objects to dependencies 48# that exist in the base system. It is frequently the case that newer objects 49# exist in the proto area that are required to satisfy other objects 50# dependencies, and without using these newer objects an ldd(1) will produce 51# misleading error messages. To compensate for this, the -d option (or the 52# existence of the CODEMSG_WS/ROOT environment variables) cause the creation of 53# alternative dependency mappings via crle(1) configuration files that establish 54# any proto shared objects as alternatives to their base system location. Thus 55# ldd(1) can be executed against these configuration files so that objects in a 56# proto area bind to their dependencies in the same proto area. 57 58 59# Define all global variables (required for strict) 60use vars qw($SkipDirs $SkipFiles $SkipTextrelFiles); 61use vars qw($SkipUndefDirs $SkipUndefFiles $SkipUnusedDirs $SkipUnusedFiles); 62use vars qw($SkipStabFiles $SkipNoExStkFiles); 63use vars qw($UnusedNoise $Prog $Mach $Isalist $Env $Ena64 $Tmpdir $Error); 64use vars qw($UnusedFiles $UnusedPaths $LddNoU $Crle32 $Crle64 $Conf32 $Conf64); 65use vars qw($SkipInterps $OldDeps %opt); 66 67use strict; 68 69 70# Define any directories we should skip completely. 71$SkipDirs = qr{ 72 etc/lib | # special - used for partial statics 73 usr/lib/devfsadm | # 4382889 74 usr/lib/libc | # optimized libc 75 usr/lib/rcm | # 4426119 76 usr/perl5 | # alan's taking care of these :-) 77 usr/src # no need to look at shipped source 78}x; 79 80# Define any files we should skip completely. 81$SkipFiles = qr{ ^(?: 82 ld\.so\.1 | # confusing but correct dependencies 83 lddstub | # lddstub has no dependencies 84 libmakestate\.so\.1 | # temporary; delivered by compiler group 85 libm\.so\.1 | # temporary; delivered by compiler group 86 libm\.so\.2 | # temporary; delivered by compiler group 87 geniconvtbl\.so | # 4384329 88 libssagent\.so\.1 | # 4328854 89 libdmi\.so\.1 | # " " 90 libdmici\.so\.1 | # " " 91 libdmimi\.so\.1 | # " " 92 libpsvcplugin_psr\.so\.1 | # 4385799 93 libpsvcpolicy_psr\.so\.1 | # " " 94 libpsvcpolicy\.so\.1 | # " " 95 picl_slm\.so | # " " 96 libcrypto_extra\.so\.0\.9\.7 | # OpenSSL SUNWcry filter lib 97 libssl_extra\.so\.0\.9\.7 | # OpenSSL SUNWcry filter lib 98 fcpackage\.so | # circular dependency on fcthread.so 99 grub 100 )$ 101}x; 102 103# Define any files that are allowed text relocations. 104$SkipTextrelFiles = qr{ ^(?: 105 unix | # kernel models are non-pic 106 mdb # relocations against __RTC (dbx) 107 )$ 108}x; 109 110# Define any files that are allowed undefined references. 111$SkipUndefDirs = qr{ 112 usr/lib/inet/ppp/ | # pppd plugins have callbacks 113 usr/lib/libp/ | # libc.so.1 requires _mcount 114 usr/lib/vold/ | # vold dependencies have callbacks 115 usr/lib/rmmount | # rmmount actions have callbacks 116 /lib/mdb/ | # mdb modules have callbacks 117 /lib/fm/fmd/plugins/ | # fmd modules have callbacks 118 /lib/fm/fmd/schemes/ # fmd schemes have callbacks 119}x; 120 121$SkipUndefFiles = qr{ ^(?: 122 libthread_db\.so\.0 | # callbacks to proc service interface 123 libthread_db\.so\.1 | # " " " " 124 librtld_db\.so\.1 | # " " " " 125 libc_db\.so\.1 | # " " " " 126 libldstab\.so\.1 | # link-edit support libraries have 127 libld\.so\.3 | # callback to the link-editors 128 libld\.so\.2 | # " " " " 129 liblddbg\.so\.4 | # " " " " 130 librtld\.so\.1 | # " " " " 131 libnisdb\.so\.2 | # C++ 132 libsvm\.so\.1 | # libspmicommon.so.1 lacking 133 libwanboot\.so\.1 | # libcrypto.a and libssl.a 134 libwrap\.so\.1\.0 | # uses symbols provided by application 135 fcthread\.so | # uses symbols provided by application 136 fn\.so\.2 | # callback to automount 137 preen_md\.so\.1 | # callback to driver 138 libike\.so\.1 | # callbacks to in.iked for IKE policy 139 devfsadmd_mod\.so | # sysevent module callback to syseventd 140 sysevent_conf_mod\.so | # sysevent module callback to syseventd 141 sysevent_reg_mod\.so # sysevent module callback to syseventd 142 )$ 143}x; 144 145# Define any files that have unused dependencies. 146$SkipUnusedDirs = qr{ 147 lib/picl/plugins/ | # require devtree dependencies 148 /lib/libp # profile libc makes libm an unused 149}x; # dependency of standard libc 150 151$SkipUnusedFiles = qr{ ^(?: 152 devfsadm | # 4382889 153 disks | # " " 154 tapes | # " " 155 ports | # " " 156 audlinks | # " " 157 devlinks | # " " 158 drvconfig | # " " 159 ntptrace | # on intel doesn't need libmd5 160 rmmount | # 4418770, volmgt dependency is required 161 # to compensate for SunPCi. 162 ocfserv | # libsched unreference by libjvm, 163 poold | # see 4952319. 164 libc\.so\.1\.9 | # 4lib/libc versions have private 165 libc\.so\.2\.9 # copies of stuff from libc. 166 )$ 167}x; 168 169# Define any files that should contain debugging information. 170$SkipStabFiles = qr{ ^(?: 171 abi_.* | 172 interceptors\.so\.1 | 173 unix 174 )$ 175}x; 176 177# Define any files that don't require a non-executable stack definition. 178$SkipNoExStkFiles = qr{ ^(?: 179 forth | 180 unix | 181 multiboot 182 )$ 183}x; 184 185# Define any files that should only have unused (ldd -u) processing. 186$UnusedPaths = qr{ 187 ucb/shutdown # libucb interposes on libc and makes 188 # dependencies on libc seem unnecessary 189}x; 190 191$UnusedFiles = qr{ ^(?: 192 rpc\.nisd # CCNEEDED makes pthread unreferenced 193 )$ 194}x; 195 196# Define unused dependencies we should ignore. 197# libCrun has a unnecessary dependency on libw, and libmapmalloc is often 198# defined to interpose on libc but isn't used by the application itself. 199# Threads dependencies look unused if libc is bound first. 200$UnusedNoise = qr{ 201 libw\.so\.1;\ unused | 202 unused\ object=.*libw\.so\.1 | 203 libthread\.so\.1;\ unused | 204 libpthread\.so\.1;\ unused | 205 unused\ object=.*libpthread\.so\.1 | 206 libnsl\.so\.1;\ unused\ dependency\ of\ .*libxslt\.so\.1 | 207 libdl\.so\.1;\ unused\ dependency\ of\ .*libspmicommon\.so\.1 | 208 libdl\.so\.1;\ unused\ dependency\ of\ .*libCrun\.so\.1 | 209 libfru\.so\.1;\ unused\ object=.*libdl\.so\.1 | 210 libfrupicl\.so\.1;\ unused\ object=.*libdl\.so\.1 | 211 libmapmalloc\.so\.1;\ unused | 212 unused\ dependency\ of\ .*libstdc\+\+\.so\.6 | 213 unreferenced\ object=.*libstdc\+\+\.so\.6 214}x; 215 216# Define interpreters we should ignore. 217$SkipInterps = qr{ 218 misc/krtld | 219 misc/amd64/krtld | 220 misc/sparcv9/krtld 221}x; 222 223# Catch libintl and libw, although ld(1) will bind to these and thus determine 224# they're needed, their content was moved into libc as of on297 build 7. 225# libthread and libpthread were completely moved into libc as of on10 build 53. 226# Also, catch libdl, whose content was moved into libc as of on10 build 49. 227$OldDeps = qr{ ^(?: 228 libintl\.so\.1 | 229 libw\.so\.1 | 230 libthread\.so\.1 | 231 libpthread\.so\.1 | 232 libdl\.so\.1 233 )$ 234}x; 235 236use Getopt::Std; 237 238# ----------------------------------------------------------------------------- 239 240# Reliably compare two OS revisions. Arguments are <ver1> <op> <ver2>. 241# <op> is the string form of a normal numeric comparison operator. 242sub cmp_os_ver { 243 my @ver1 = split(/\./, $_[0]); 244 my $op = $_[1]; 245 my @ver2 = split(/\./, $_[2]); 246 247 push @ver2, ("0") x $#ver1 - $#ver2; 248 push @ver1, ("0") x $#ver2 - $#ver1; 249 250 my $diff = 0; 251 while (@ver1 || @ver2) { 252 if (($diff = shift(@ver1) - shift(@ver2)) != 0) { 253 last; 254 } 255 } 256 return (eval "$diff $op 0" ? 1 : 0); 257} 258 259# Establish a program name for any error diagnostics. 260chomp($Prog = `basename $0`); 261 262# Determine what machinery is available. 263$Mach = `uname -p`; 264$Isalist = `isalist`; 265$Env = ""; 266if ($Mach =~ /sparc/) { 267 if ($Isalist =~ /sparcv9/) { 268 $Ena64 = "ok"; 269 } 270} elsif ($Mach =~ /i386/) { 271 if ($Isalist =~ /amd64/) { 272 $Ena64 = "ok"; 273 } 274} 275 276# Check that we have arguments. 277if ((getopts('ad:imos', \%opt) == 0) || ($#ARGV == -1)) { 278 print "usage: $Prog [-a] [-d depdir] [-m] [-o] [-s] file | dir, ...\n"; 279 print "\t[-a]\t\tprocess all files (ignore any exception lists)\n"; 280 print "\t[-d dir]\testablish dependencies from under directory\n"; 281 print "\t[-i]\t\tproduce dynamic table entry information\n"; 282 print "\t[-m]\t\tprocess mcs(1) comments\n"; 283 print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n"; 284 print "\t[-s]\t\tprocess .stab and .symtab entries\n"; 285 exit 1; 286} else { 287 my($Proto); 288 289 if ($opt{d}) { 290 # User specified dependency directory - make sure it exists. 291 if (! -d $opt{d}) { 292 print "$Prog: $opt{d} is not a directory\n"; 293 exit 1; 294 } 295 $Proto = $opt{d}; 296 297 } elsif ($ENV{CODEMGR_WS}) { 298 my($Root); 299 300 # Without a user specified dependency directory see if we're 301 # part of a codemanager workspace and if a proto area exists. 302 if (($Root = $ENV{ROOT}) && (-d $Root)) { 303 $Proto = $Root; 304 } 305 } 306 307 if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir)) { 308 $Tmpdir = "/tmp"; 309 } 310 311 # Look for dependencies under $Proto. 312 if ($Proto) { 313 # To support alternative dependency mapping we'll need ldd(1)'s 314 # -e option. This is relatively new (s81_30), so make sure 315 # ldd(1)is capable before gathering any dependency information. 316 if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) { 317 print "ldd: does not support -e, unable to "; 318 print "create alternative dependency mappingings.\n"; 319 print "ldd: option added under 4390308 (s81_30).\n\n"; 320 } else { 321 # Gather dependencies and construct a alternative 322 # dependency mapping via a crle(1) configuration file. 323 GetDeps($Proto, "/"); 324 GenConf(); 325 } 326 } 327 328 # To support unreferenced dependency detection we'll need ldd(1)'s -U 329 # option. This is relatively new (4638070), and if not available we 330 # can still fall back to -u. Even with this option, don't use -U with 331 # releases prior to 5.10 as the cleanup for -U use only got integrated 332 # into 5.10 under 4642023. Note, that nightly doesn't typically set a 333 # RELEASE from the standard <env> files. Users who wish to disable use 334 # of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file 335 # if using nightly, or otherwise establish it in their environment. 336 if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) { 337 $LddNoU = 1; 338 } else { 339 my($Release); 340 341 if (($Release = $ENV{RELEASE}) && 342 (cmp_os_ver($Release, "<", "5.10"))) { 343 $LddNoU = 1; 344 } else { 345 $LddNoU = 0; 346 } 347 } 348 349 # For each argument determine if we're dealing with a file or directory. 350 foreach my $Arg (@ARGV) { 351 # Ignore symbolic links. 352 if (-l $Arg) { 353 next; 354 } 355 356 if (!stat($Arg)) { 357 next; 358 } 359 360 # Process simple files. 361 if (-f _) { 362 my($RelPath) = $Arg; 363 my($File) = $Arg; 364 my($Secure) = 0; 365 366 $RelPath =~ s!^.*/!./!; 367 $File =~ s!^.*/!!; 368 369 if (-u _ || -g _) { 370 $Secure = 1; 371 } 372 373 ProcFile($Arg, $RelPath, $File, $Secure); 374 next; 375 } 376 # Process directories. 377 if (-d _) { 378 ProcDir($Arg, "."); 379 next; 380 } 381 382 print "$Arg is not a file or directory\n"; 383 $Error = 1; 384 } 385 386 # Cleanup 387 CleanUp(); 388} 389 390$Error = 0; 391 392# Clean up and temporary files. 393sub CleanUp { 394 if ($Crle64) { 395 unlink $Crle64; 396 } 397 if ($Conf64) { 398 unlink $Conf64; 399 } 400 if ($Crle32) { 401 unlink $Crle32; 402 } 403 if ($Conf32) { 404 unlink $Conf32; 405 } 406} 407 408# Create an output message, either a one-liner (under -o) or preceded by the 409# files relative pathname as a title. 410sub OutMsg { 411 my($Ttl, $Path, $Msg) = @_; 412 413 if ($opt{o}) { 414 $Msg =~ s/^[ \t]*//; 415 print "$Path: $Msg\n"; 416 } else { 417 if ($Ttl eq 0) { 418 print "==== $Path ====\n"; 419 } 420 print "$Msg\n"; 421 } 422} 423 424# Determine whether this a ELF dynamic object and if so investigate its runtime 425# attributes. 426sub ProcFile { 427 my($FullPath, $RelPath, $File, $Secure) = @_; 428 my(@Elf, @Ldd, $Dyn, $Intp, $Dll, $Ttl, $Sym, $Interp, $Stack); 429 my($Sun, $Relsz, $Pltsz, $Uns, $Tex, $Stab, $Strip, $Lddopt); 430 my($Val, $Header, $SkipLdd, $IsX86, $RWX); 431 432 # Ignore symbolic links. 433 if (-l $FullPath) { 434 return; 435 } 436 437 $Ttl = 0; 438 @Ldd = 0; 439 440 # Determine whether we have access to inspect the file. 441 if (!(-r $FullPath)) { 442 OutMsg($Ttl++, $RelPath, 443 "\tunable to inspect file: permission denied"); 444 return; 445 } 446 447 # Determine if this is a file we don't care about. 448 if (!$opt{a}) { 449 if ($File =~ $SkipFiles) { 450 return; 451 } 452 } 453 454 # Determine whether we have a executable (static or dynamic) or a 455 # shared object. 456 @Elf = split(/\n/, `elfdump -epdic $FullPath 2>&1`); 457 458 $Dyn = $Intp = $Dll = $Stack = $IsX86 = $RWX = 0; 459 $Interp = 1; 460 $Header = 'None'; 461 foreach my $Line (@Elf) { 462 # If we have an invalid file type (which we can tell from the 463 # first line), or we're processing an archive, bail. 464 if ($Header eq 'None') { 465 if (($Line =~ /invalid file/) || 466 ($Line =~ /$FullPath(.*):/)) { 467 return; 468 } 469 } 470 471 if ($Line =~ /^ELF Header/) { 472 $Header = 'Ehdr'; 473 474 } elsif ($Line =~ /^Program Header/) { 475 $Header = 'Phdr'; 476 $RWX = 0; 477 478 } elsif ($Line =~ /^Interpreter/) { 479 $Header = 'Intp'; 480 481 } elsif ($Line =~ /^Dynamic Section/) { 482 # A dynamic section indicates we're a dynamic object 483 # (this makes sure we don't check static executables). 484 $Dyn = 1; 485 486 } elsif (($Header eq 'Ehdr') && ($Line =~ /e_type:/)) { 487 # The e_type field indicates whether this file is a 488 # shared object (ET_DYN) or an executable (ET_EXEC). 489 if ($Line =~ /ET_DYN/) { 490 $Dll = 1; 491 } elsif ($Line !~ /ET_EXEC/) { 492 return; 493 } 494 } elsif (($Header eq 'Ehdr') && ($Line =~ /ei_class:/)) { 495 # If we encounter a 64-bit object, but we're not running 496 # on a 64-bit system, suppress calling ldd(1). 497 if (($Line =~ /ELFCLASS64/) && !$Ena64) { 498 $SkipLdd = 1; 499 } 500 } elsif (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) { 501 # If it's a X86 object, we need to enforce RW- data. 502 if (($Line =~ /(EM_AMD64|EM_386)/)) { 503 $IsX86 = 1; 504 } 505 } elsif (($Header eq 'Phdr') && 506 ($Line =~ /\[ PF_X PF_W PF_R \]/)) { 507 # RWX segment seen. 508 $RWX = 1; 509 510 } elsif (($Header eq 'Phdr') && 511 ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) { 512 # Seen an RWX PT_LOAD segment. 513 if ($File !~ $SkipNoExStkFiles) { 514 OutMsg($Ttl++, $RelPath, 515 "\tapplication requires non-executable " . 516 "data\t<no -Mmapfile_noexdata?>"); 517 } 518 519 } elsif (($Header eq 'Phdr') && 520 ($Line =~ /\[ PT_SUNWSTACK \]/)) { 521 # This object defines a non-executable stack. 522 $Stack = 1; 523 524 } elsif (($Header eq 'Intp') && !$opt{a} && 525 ($Line =~ $SkipInterps)) { 526 # This object defines an interpretor we should skip. 527 $Interp = 0; 528 } 529 } 530 531 # Determine whether this ELF executable or shared object has a 532 # conforming mcs(1) comment section. If the correct $(POST_PROCESS) 533 # macros are used, only a 3 or 4 line .comment section should exist 534 # containing one or two "@(#)SunOS" identifying comments (one comment 535 # for a non-debug build, and two for a debug build). The results of 536 # the following split should be three or four lines, the last empty 537 # line being discarded by the split. 538 if ($opt{m}) { 539 my(@Mcs, $Con, $Dev); 540 541 @Mcs = split(/\n/, `mcs -p $FullPath 2>&1`); 542 543 $Con = $Dev = $Val = 0; 544 foreach my $Line (@Mcs) { 545 $Val++; 546 547 if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) { 548 $Con = 1; 549 last; 550 } 551 if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) { 552 $Dev = 1; 553 next; 554 } 555 if (($Dev == 0) && ($Val == 4)) { 556 $Con = 1; 557 last; 558 } 559 if (($Dev == 1) && ($Val == 5)) { 560 $Con = 1; 561 last; 562 } 563 } 564 if ($opt{m} && ($Con == 1)) { 565 OutMsg($Ttl++, $RelPath, 566 "\tnon-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>"); 567 } 568 } 569 570 # Applications should contain a non-executable stack definition. 571 if (($Dll == 0) && ($Stack == 0)) { 572 if (!$opt{a}) { 573 if ($File =~ $SkipNoExStkFiles) { 574 goto DYN; 575 } 576 } 577 OutMsg($Ttl++, $RelPath, 578 "\tapplication requires non-executable stack\t<no -Mmapfile_noexstk?>"); 579 } 580 581DYN: 582 # Having caught any static executables in the mcs(1) check and non- 583 # executable stack definition check, continue with dynamic objects 584 # from now on. 585 if ($Dyn eq 0) { 586 return; 587 } 588 589 # Only use ldd unless we've encountered an interpreter that should 590 # ne skipped. 591 if (!$SkipLdd && $Interp) { 592 if ($Secure) { 593 # The execution of a secure application over an nfs file 594 # system mounted nosuid will result in warning messages 595 # being sent to /var/adm/messages. As this type of 596 # environment can occur with root builds, move the file 597 # being investigated to a safe place first. In addition 598 # remove its secure permission so that it can be 599 # influenced by any alternative dependency mappings. 600 601 my($TmpPath) = "$Tmpdir/$File"; 602 603 system('cp', $FullPath, $TmpPath); 604 chmod 0777, $TmpPath; 605 $FullPath = $TmpPath; 606 } 607 608 # Use ldd(1) to determine the objects relocatability and use. 609 # By default look for all unreferenced dependencies. However, 610 # some objects have legitimate dependencies that they do not 611 # reference. 612 if ($LddNoU || ($File =~ $UnusedFiles) || 613 ($RelPath =~ $UnusedPaths)) { 614 $Lddopt = "-ru"; 615 } else { 616 $Lddopt = "-rU"; 617 } 618 @Ldd = split(/\n/, `ldd $Lddopt $Env $FullPath 2>&1`); 619 if ($Secure) { 620 unlink $FullPath; 621 } 622 } 623 624 $Val = 0; 625 $Sym = 5; 626 $Uns = 1; 627 628LDD: foreach my $Line (@Ldd) { 629 630 if ($Val == 0) { 631 $Val = 1; 632 # Make sure ldd(1) worked. One possible failure is that 633 # this is an old ldd(1) prior to -e addition (4390308). 634 if ($Line =~ /usage:/) { 635 $Line =~ s/$/\t<old ldd(1)?>/; 636 OutMsg($Ttl++, $RelPath, $Line); 637 last; 638 } elsif ($Line =~ /execution failed/) { 639 OutMsg($Ttl++, $RelPath, $Line); 640 last; 641 } 642 643 # It's possible this binary can't be executed, ie. we've 644 # found a sparc binary while running on an intel system, 645 # or a sparcv9 binary on a sparcv7/8 system. 646 if ($Line =~ /wrong class/) { 647 OutMsg($Ttl++, $RelPath, 648 "\thas wrong class or data encoding"); 649 next; 650 } 651 652 # Historically, ldd(1) likes executable objects to have 653 # their execute bit set. Note that this test isn't 654 # applied unless the -a option is in effect, as any 655 # non-executable files are skipped by default to reduce 656 # the cost of running this script. 657 if ($Line =~ /not executable/) { 658 OutMsg($Ttl++, $RelPath, 659 "\tis not executable"); 660 next; 661 } 662 } 663 664 # Look for "file" or "versions" that aren't found. Note that 665 # these lines will occur before we find any symbol referencing 666 # errors. 667 if (($Sym == 5) && ($Line =~ /not found\)/)) { 668 if ($Line =~ /file not found\)/) { 669 $Line =~ s/$/\t<no -zdefs?>/; 670 } 671 OutMsg($Ttl++, $RelPath, $Line); 672 next; 673 } 674 # Look for relocations whose symbols can't be found. Note, we 675 # only print out the first 5 relocations for any file as this 676 # output can be excessive. 677 if ($Sym && ($Line =~ /symbol not found/)) { 678 # Determine if this file is allowed undefined 679 # references. 680 if ($Sym == 5) { 681 if (!$opt{a}) { 682 if ($RelPath =~ $SkipUndefDirs) { 683 $Sym = 0; 684 next LDD; 685 } 686 if ($File =~ $SkipUndefFiles) { 687 $Sym = 0; 688 next LDD; 689 } 690 } 691 } 692 if ($Sym-- == 1) { 693 if (!$opt{o}) { 694 OutMsg($Ttl++, $RelPath, 695 "\tcontinued ..."); 696 } 697 next; 698 } 699 # Just print the symbol name. 700 $Line =~ s/$/\t<no -zdefs?>/; 701 OutMsg($Ttl++, $RelPath, $Line); 702 next; 703 } 704 # Look for any unused dependencies. 705 if ($Uns && ($Line =~ /unused/)) { 706 if (!$opt{a}) { 707 if ($RelPath =~ $SkipUnusedDirs) { 708 $Uns = 0; 709 next LDD; 710 } 711 if ($File =~ $SkipUnusedFiles) { 712 $Uns = 0; 713 next LDD; 714 } 715 716 # Remove any noise. 717 if ($Line =~ $UnusedNoise) { 718 $Uns = 0; 719 next LDD; 720 } 721 } 722 if ($Secure) { 723 $Line =~ s!$Tmpdir/!!; 724 } 725 $Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/; 726 OutMsg($Ttl++, $RelPath, $Line); 727 next; 728 } 729 } 730 731 # Reuse the elfdump(1) data to investigate additional dynamic linking 732 # information. 733 734 $Sun = $Relsz = $Pltsz = $Dyn = $Stab = 0; 735 $Tex = $Strip = 1; 736 737 $Header = 'None'; 738ELF: foreach my $Line (@Elf) { 739 # We're only interested in the section headers and the dynamic 740 # section. 741 if ($Line =~ /^Section Header/) { 742 $Header = 'Shdr'; 743 744 if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) { 745 # This object has a combined relocation section. 746 $Sun = 1; 747 748 } elsif (($Stab == 0) && ($Line =~ /\.stab/)) { 749 # This object contain .stabs sections 750 $Stab = 1; 751 } 752 753 if (($Strip == 1) && ($Line =~ /\.symtab/)) { 754 # This object contains a complete symbol table. 755 $Strip = 0; 756 } 757 next; 758 759 } elsif ($Line =~ /^Dynamic Section/) { 760 $Header = 'Dyn'; 761 next; 762 } elsif ($Header ne 'Dyn') { 763 next; 764 } 765 766 # Does this object contain text relocations. 767 if ($Tex && ($Line =~ /TEXTREL/)) { 768 # Determine if this file is allowed text relocations. 769 if (!$opt{a}) { 770 if ($File =~ $SkipTextrelFiles) { 771 $Tex = 0; 772 next ELF; 773 } 774 } 775 OutMsg($Ttl++, $RelPath, 776 "\tTEXTREL .dynamic tag\t\t\t<no -Kpic?>"); 777 $Tex = 0; 778 next; 779 } 780 781 # Does this file have any relocation sections (there are a few 782 # psr libraries with no relocations at all, thus a .SUNW_reloc 783 # section won't exist either). 784 if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) { 785 $Relsz = hex((split(' ', $Line))[2]); 786 next; 787 } 788 789 # Does this file have any plt relocations. If the plt size is 790 # equivalent to the total relocation size then we don't have 791 # any relocations suitable for combining into a .SUNW_reloc 792 # section. 793 if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) { 794 $Pltsz = hex((split(' ', $Line))[2]); 795 next; 796 } 797 798 # Under the -i (information) option print out any useful dynamic 799 # entries. 800 # Does this object have any dependencies. 801 if ($opt{i} && ($Line =~ /NEEDED/)) { 802 my($Need) = (split(' ', $Line))[3]; 803 804 # Catch any old (unnecessary) dependencies. 805 if ($Need =~ $OldDeps) { 806 OutMsg($Ttl++, $RelPath, 807 "\tNEEDED=$Need\t<dependency no longer necessary>"); 808 } else { 809 OutMsg($Ttl++, $RelPath, "\tNEEDED=$Need"); 810 } 811 next; 812 } 813 814 # Does this object specify a runpath. 815 if ($opt{i} && ($Line =~ /RPATH/)) { 816 my($Rpath) = (split(' ', $Line))[3]; 817 OutMsg($Ttl++, $RelPath, "\tRPATH=$Rpath"); 818 next; 819 } 820 } 821 822 # A shared object, that contains non-plt relocations, should have a 823 # combined relocation section indicating it was built with -z combreloc. 824 if ($Dll && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) { 825 OutMsg($Ttl++, $RelPath, 826 "\tSUNW_reloc section missing\t\t<no -zcombreloc?>"); 827 } 828 829 # No objects released to a customer should have any .stabs sections 830 # remaining, they should be stripped. 831 if ($opt{s} && $Stab) { 832 if (!$opt{a}) { 833 if ($File =~ $SkipStabFiles) { 834 goto DONESTAB; 835 } 836 } 837 OutMsg($Ttl++, $RelPath, 838 "\tdebugging sections should be deleted\t<no strip -x?>"); 839 } 840 841DONESTAB: 842 843 # All objects should have a full symbol table to provide complete 844 # debugging stack traces. 845 if ($Strip) { 846 OutMsg($Ttl++, $RelPath, 847 "\tsymbol table should not be stripped\t<remove -s?>"); 848 } 849} 850 851 852sub ProcDir { 853 my($FullDir, $RelDir) = @_; 854 my($NewFull, $NewRel); 855 856 # Determine if this is a directory we don't care about. 857 if (!$opt{a}) { 858 if ($RelDir =~ $SkipDirs) { 859 return; 860 } 861 } 862 863 # Open the directory and read each entry, omit files starting with "." 864 if (opendir(DIR, $FullDir)) { 865 foreach my $Entry (readdir(DIR)) { 866 if ($Entry =~ /^\./) { 867 next; 868 } 869 $NewFull = "$FullDir/$Entry"; 870 871 # Ignore symlinks. 872 if (-l $NewFull) { 873 next; 874 } 875 if (!stat($NewFull)) { 876 next; 877 } 878 $NewRel = "$RelDir/$Entry"; 879 880 # Descend into and process any directories. 881 if (-d _) { 882 ProcDir($NewFull, $NewRel); 883 next; 884 } 885 886 # Typically dynamic objects are executable, so we can 887 # reduce the overall cost of this script (a lot!) by 888 # screening out non-executables here, rather than pass 889 # them to file(1) later. However, it has been known 890 # for shared objects to be mistakenly left non- 891 # executable, so with -a let all files through so that 892 # this requirement can be verified (see ProcFile()). 893 if (!$opt{a}) { 894 if (! -x _) { 895 next; 896 } 897 } 898 899 # Process any standard files. 900 if (-f _) { 901 my($Secure) = 0; 902 903 if (-u _ || -g _) { 904 $Secure = 1; 905 } 906 907 ProcFile($NewFull, $NewRel, $Entry, $Secure); 908 next; 909 } 910 911 } 912 closedir(DIR); 913 } 914} 915 916# Create a crle(1) script for any 64-bit dependencies we locate. A runtime 917# configuration file will be generated to establish alternative dependency 918# mappings for all these dependencies. 919 920sub Entercrle64 { 921 my($FullDir, $RelDir, $Entry) = @_; 922 923 if (!$Crle64) { 924 # Create and initialize the script if is doesn't already exit. 925 926 $Crle64 = "$Tmpdir/$Prog.crle64.$$"; 927 open(CRLE64, "> $Crle64") || 928 die "$Prog: open failed: $Crle64: $!"; 929 930 print CRLE64 "#!/bin/sh\ncrle -64\\\n"; 931 } 932 print CRLE64 "\t-o $FullDir -a $RelDir/$Entry \\\n"; 933} 934 935# Create a crle(1) script for any 32-bit dependencies we locate. A runtime 936# configuration file will be generated to establish alternative dependency 937# mappings for all these dependencies. 938 939sub Entercrle32 { 940 my($FullDir, $RelDir, $Entry) = @_; 941 942 if (!$Crle32) { 943 # Create and initialize the script if is doesn't already exit. 944 945 $Crle32 = "$Tmpdir/$Prog.crle32.$$"; 946 open(CRLE32, "> $Crle32") || 947 die "$Prog: open failed: $Crle32: $!"; 948 949 print CRLE32 "#!/bin/sh\ncrle \\\n"; 950 } 951 print CRLE32 "\t-o $FullDir -a $RelDir/$Entry \\\n"; 952} 953 954# Having finished gathering dependencies, complete any crle(1) scripts and 955# execute them to generate the associated runtime configuration files. In 956# addition establish the environment variable required to pass the configuration 957# files to ldd(1). 958 959sub GenConf { 960 if ($Crle64) { 961 $Conf64 = "$Tmpdir/$Prog.conf64.$$"; 962 print CRLE64 "\t-c $Conf64\n"; 963 964 chmod 0755, $Crle64; 965 close CRLE64; 966 967 if (system($Crle64)) { 968 undef $Conf64; 969 } 970 } 971 if ($Crle32) { 972 $Conf32 = "$Tmpdir/$Prog.conf32.$$"; 973 print CRLE32 "\t-c $Conf32\n"; 974 975 chmod 0755, $Crle32; 976 close CRLE32; 977 978 if (system($Crle32)) { 979 undef $Conf32; 980 } 981 } 982 983 if ($Crle64 && $Conf64 && $Crle32 && $Conf32) { 984 $Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32"; 985 } elsif ($Crle64 && $Conf64) { 986 $Env = "-e LD_FLAGS=config_64=$Conf64"; 987 } elsif ($Crle32 && $Conf32) { 988 $Env = "-e LD_FLAGS=config_32=$Conf32"; 989 } 990} 991 992# Recurse through a directory hierarchy looking for appropriate dependencies. 993 994sub GetDeps { 995 my($FullDir, $RelDir) = @_; 996 my($NewFull); 997 998 # Open the directory and read each entry, omit files starting with "." 999 if (opendir(DIR, $FullDir)) { 1000 foreach my $Entry (readdir(DIR)) { 1001 if ($Entry =~ /^\./) { 1002 next; 1003 } 1004 $NewFull = "$FullDir/$Entry"; 1005 1006 # We need to follow links so that any dependencies 1007 # are expressed in all their available forms. 1008 # Bail on symlinks like 32 -> . 1009 if (-l $NewFull) { 1010 if (readlink($NewFull) =~ /^\.$/) { 1011 next; 1012 } 1013 } 1014 if (!stat($NewFull)) { 1015 next; 1016 } 1017 1018 # If this is a directory descend into it. 1019 if (-d _) { 1020 my($NewRel); 1021 1022 if ($RelDir =~ /^\/$/) { 1023 $NewRel = "$RelDir$Entry"; 1024 } else { 1025 $NewRel = "$RelDir/$Entry"; 1026 } 1027 1028 GetDeps($NewFull, $NewRel); 1029 next; 1030 } 1031 1032 # If this is a regular file determine if its a 1033 # valid ELF dependency. 1034 if (-f _) { 1035 my($File); 1036 1037 # Typically shared object dependencies end with 1038 # ".so" or ".so.?", hence we can reduce the cost 1039 # of this script (a lot!) by screening out files 1040 # that don't follow this pattern. 1041 if (!$opt{a}) { 1042 if ($Entry !~ /\.so(?:\.\d+)*$/) { 1043 next; 1044 } 1045 } 1046 1047 $File = `file $NewFull`; 1048 if ($File !~ /dynamic lib/) { 1049 next; 1050 } 1051 1052 if ($File =~ /32-bit/) { 1053 Entercrle32($FullDir, $RelDir, $Entry); 1054 } elsif ($Ena64) { 1055 Entercrle64($FullDir, $RelDir, $Entry); 1056 } 1057 next; 1058 } 1059 } 1060 closedir(DIR); 1061 } 1062} 1063exit $Error 1064