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