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