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