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