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