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 2009 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. 39# 40# By default any file that has conditions that should be reported is first 41# listed and then each condition follows. The -o (one-line) option produces a 42# more terse output which is better for sorting/diffing with "nightly". 43# 44# NOTE: missing dependencies, symbols or versions are reported by running the 45# file through ldd(1). As objects within a proto area are built to exist in a 46# base system, standard use of ldd(1) will bind any objects to dependencies 47# that exist in the base system. It is frequently the case that newer objects 48# exist in the proto area that are required to satisfy other objects 49# dependencies, and without using these newer objects an ldd(1) will produce 50# misleading error messages. To compensate for this, the -D/-d options, or the 51# existence of the CODEMSG_WS/ROOT environment variables, cause the creation of 52# alternative dependency mappings via crle(1) configuration files that establish 53# any proto shared objects as alternatives to their base system location. Thus 54# ldd(1) can be executed against these configuration files so that objects in a 55# proto area bind to their dependencies in the same proto area. 56 57 58# Define all global variables (required for strict) 59use vars qw($Prog $Env $Ena64 $Tmpdir $Gnuc); 60use vars qw($LddNoU $Conf32 $Conf64); 61use vars qw(%opt); 62use vars qw($ErrFH $ErrTtl $InfoFH $InfoTtl $OutCnt1 $OutCnt2); 63 64# An exception file is used to specify regular expressions to match 65# objects. These directives specify special attributes of the object. 66# The regular expressions are read from the file and compiled into the 67# regular expression variables. 68# 69# The name of each regular expression variable is of the form 70# 71# $EXRE_xxx 72# 73# where xxx is the name of the exception in lower case. For example, 74# the regular expression variable for EXEC_STACK is $EXRE_exec_stack. 75# 76# onbld_elfmod::LoadExceptionsToEXRE() depends on this naming convention 77# to initialize the regular expression variables, and to detect invalid 78# exception names. 79# 80# If a given exception is not used in the exception file, its regular 81# expression variable will be undefined. Users of these variables must 82# test the variable with defined() prior to use: 83# 84# defined($EXRE_exec_stack) && ($foo =~ $EXRE_exec_stack) 85# 86# ---- 87# 88# The exceptions are: 89# 90# EXEC_STACK 91# Objects that are not required to have a non-executable stack 92# 93# NOCRLEALT 94# Objects that should be skipped by AltObjectConfig() when building 95# the crle script that maps objects to the proto area. 96# 97# NODIRECT 98# Objects that are not required to use direct bindings 99# 100# NOSYMSORT 101# Objects we should not check for duplicate addresses in 102# the symbol sort sections. 103# 104# OLDDEP 105# Objects that are no longer needed because their functionalty 106# has migrated elsewhere. These are usually pure filters that 107# point at libc. 108# 109# SKIP 110# Files and directories that should be excluded from analysis. 111# 112# STAB 113# Objects that are allowed to contain stab debugging sections 114# 115# TEXTREL 116# Object for which relocations are allowed to the text segment 117# 118# UNDEF_REF 119# Objects that are allowed undefined references 120# 121# UNREF_OBJ 122# "unreferenced object=" ldd(1) diagnostics. 123# 124# UNUSED_DEPS 125# Objects that are allowed to have unused dependencies 126# 127# UNUSED_OBJ 128# Objects that are allowed to be unused dependencies 129# 130# UNUSED_RPATH 131# Objects with unused runpaths 132# 133 134use vars qw($EXRE_exec_stack $EXRE_nocrlealt $EXRE_nodirect $EXRE_nosymsort); 135use vars qw($EXRE_olddep $EXRE_skip $EXRE_stab $EXRE_textrel $EXRE_undef_ref); 136use vars qw($EXRE_unref_obj $EXRE_unused_deps $EXRE_unused_obj); 137use vars qw($EXRE_unused_rpath); 138 139use strict; 140use Getopt::Std; 141use File::Basename; 142 143 144# Reliably compare two OS revisions. Arguments are <ver1> <op> <ver2>. 145# <op> is the string form of a normal numeric comparison operator. 146sub cmp_os_ver { 147 my @ver1 = split(/\./, $_[0]); 148 my $op = $_[1]; 149 my @ver2 = split(/\./, $_[2]); 150 151 push @ver2, ("0") x $#ver1 - $#ver2; 152 push @ver1, ("0") x $#ver2 - $#ver1; 153 154 my $diff = 0; 155 while (@ver1 || @ver2) { 156 if (($diff = shift(@ver1) - shift(@ver2)) != 0) { 157 last; 158 } 159 } 160 return (eval "$diff $op 0" ? 1 : 0); 161} 162 163## ProcFile(FullPath, RelPath, File, Class, Type, Verdef) 164# 165# Determine whether this a ELF dynamic object and if so investigate its runtime 166# attributes. 167# 168sub ProcFile { 169 my($FullPath, $RelPath, $Class, $Type, $Verdef) = @_; 170 my(@Elf, @Ldd, $Dyn, $Sym, $Stack); 171 my($Sun, $Relsz, $Pltsz, $Tex, $Stab, $Strip, $Lddopt, $SymSort); 172 my($Val, $Header, $IsX86, $RWX, $UnDep); 173 my($HasDirectBinding); 174 175 # Only look at executables and sharable objects 176 return if ($Type ne 'EXEC') && ($Type ne 'DYN'); 177 178 # Ignore symbolic links 179 return if -l $FullPath; 180 181 # Is this an object or directory hierarchy we don't care about? 182 return if (defined($EXRE_skip) && ($RelPath =~ $EXRE_skip)); 183 184 # Bail if we can't stat the file. Otherwise, note if it is SUID/SGID. 185 return if !stat($FullPath); 186 my $Secure = (-u _ || -g _) ? 1 : 0; 187 188 # Reset output message counts for new input file 189 $$ErrTtl = $$InfoTtl = 0; 190 191 @Ldd = 0; 192 193 # Determine whether we have access to inspect the file. 194 if (!(-r $FullPath)) { 195 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 196 "unable to inspect file: permission denied"); 197 return; 198 } 199 200 # Determine whether we have a executable (static or dynamic) or a 201 # shared object. 202 @Elf = split(/\n/, `elfdump -epdcy $FullPath 2>&1`); 203 204 $Dyn = $Stack = $IsX86 = $RWX = 0; 205 $Header = 'None'; 206 foreach my $Line (@Elf) { 207 # If we have an invalid file type (which we can tell from the 208 # first line), or we're processing an archive, bail. 209 if ($Header eq 'None') { 210 if (($Line =~ /invalid file/) || 211 ($Line =~ /$FullPath(.*):/)) { 212 return; 213 } 214 } 215 216 if ($Line =~ /^ELF Header/) { 217 $Header = 'Ehdr'; 218 next; 219 } 220 221 if ($Line =~ /^Program Header/) { 222 $Header = 'Phdr'; 223 $RWX = 0; 224 next; 225 } 226 227 if ($Line =~ /^Dynamic Section/) { 228 # A dynamic section indicates we're a dynamic object 229 # (this makes sure we don't check static executables). 230 $Dyn = 1; 231 next; 232 } 233 234 if (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) { 235 # If it's a X86 object, we need to enforce RW- data. 236 $IsX86 = 1 if $Line =~ /(EM_AMD64|EM_386)/; 237 next; 238 } 239 240 if (($Header eq 'Phdr') && 241 ($Line =~ /\[ PF_X PF_W PF_R \]/)) { 242 # RWX segment seen. 243 $RWX = 1; 244 next; 245 } 246 247 if (($Header eq 'Phdr') && 248 ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) { 249 # Seen an RWX PT_LOAD segment. 250 if (defined($EXRE_exec_stack) && 251 ($RelPath !~ $EXRE_exec_stack)) { 252 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 253 "application requires non-executable " . 254 "data\t<no -Mmapfile_noexdata?>"); 255 } 256 next; 257 } 258 259 if (($Header eq 'Phdr') && ($Line =~ /\[ PT_SUNWSTACK \]/)) { 260 # This object defines a non-executable stack. 261 $Stack = 1; 262 next; 263 } 264 } 265 266 # Determine whether this ELF executable or shared object has a 267 # conforming mcs(1) comment section. If the correct $(POST_PROCESS) 268 # macros are used, only a 3 or 4 line .comment section should exist 269 # containing one or two "@(#)SunOS" identifying comments (one comment 270 # for a non-debug build, and two for a debug build). The results of 271 # the following split should be three or four lines, the last empty 272 # line being discarded by the split. 273 if ($opt{m}) { 274 my(@Mcs, $Con, $Dev); 275 276 @Mcs = split(/\n/, `mcs -p $FullPath 2>&1`); 277 278 $Con = $Dev = $Val = 0; 279 foreach my $Line (@Mcs) { 280 $Val++; 281 282 if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) { 283 $Con = 1; 284 last; 285 } 286 if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) { 287 $Dev = 1; 288 next; 289 } 290 if (($Dev == 0) && ($Val == 4)) { 291 $Con = 1; 292 last; 293 } 294 if (($Dev == 1) && ($Val == 5)) { 295 $Con = 1; 296 last; 297 } 298 } 299 if ($opt{m} && ($Con == 1)) { 300 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 301 "non-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>"); 302 } 303 } 304 305 # Applications should contain a non-executable stack definition. 306 if (($Type eq 'EXEC') && ($Stack == 0) && 307 (!defined($EXRE_exec_stack) || ($RelPath !~ $EXRE_exec_stack))) { 308 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 309 "non-executable stack required\t<no -Mmapfile_noexstk?>"); 310 } 311 312 # Having caught any static executables in the mcs(1) check and non- 313 # executable stack definition check, continue with dynamic objects 314 # from now on. 315 if ($Dyn eq 0) { 316 return; 317 } 318 319 # Use ldd unless its a 64-bit object and we lack the hardware. 320 if (($Class == 32) || $Ena64) { 321 my $LDDFullPath = $FullPath; 322 323 if ($Secure) { 324 # The execution of a secure application over an nfs file 325 # system mounted nosuid will result in warning messages 326 # being sent to /var/adm/messages. As this type of 327 # environment can occur with root builds, move the file 328 # being investigated to a safe place first. In addition 329 # remove its secure permission so that it can be 330 # influenced by any alternative dependency mappings. 331 332 my $File = $RelPath; 333 $File =~ s!^.*/!!; # basename 334 335 my($TmpPath) = "$Tmpdir/$File"; 336 337 system('cp', $LDDFullPath, $TmpPath); 338 chmod 0777, $TmpPath; 339 $LDDFullPath = $TmpPath; 340 } 341 342 # Use ldd(1) to determine the objects relocatability and use. 343 # By default look for all unreferenced dependencies. However, 344 # some objects have legitimate dependencies that they do not 345 # reference. 346 if ($LddNoU) { 347 $Lddopt = "-ru"; 348 } else { 349 $Lddopt = "-rU"; 350 } 351 @Ldd = split(/\n/, `ldd $Lddopt $Env $LDDFullPath 2>&1`); 352 if ($Secure) { 353 unlink $LDDFullPath; 354 } 355 } 356 357 $Val = 0; 358 $Sym = 5; 359 $UnDep = 1; 360 361 foreach my $Line (@Ldd) { 362 363 if ($Val == 0) { 364 $Val = 1; 365 # Make sure ldd(1) worked. One possible failure is that 366 # this is an old ldd(1) prior to -e addition (4390308). 367 if ($Line =~ /usage:/) { 368 $Line =~ s/$/\t<old ldd(1)?>/; 369 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, 370 $RelPath, $Line); 371 last; 372 } elsif ($Line =~ /execution failed/) { 373 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, 374 $RelPath, $Line); 375 last; 376 } 377 378 # It's possible this binary can't be executed, ie. we've 379 # found a sparc binary while running on an intel system, 380 # or a sparcv9 binary on a sparcv7/8 system. 381 if ($Line =~ /wrong class/) { 382 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 383 "has wrong class or data encoding"); 384 next; 385 } 386 387 # Historically, ldd(1) likes executable objects to have 388 # their execute bit set. 389 if ($Line =~ /not executable/) { 390 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 391 "is not executable"); 392 next; 393 } 394 } 395 396 # Look for "file" or "versions" that aren't found. Note that 397 # these lines will occur before we find any symbol referencing 398 # errors. 399 if (($Sym == 5) && ($Line =~ /not found\)/)) { 400 if ($Line =~ /file not found\)/) { 401 $Line =~ s/$/\t<no -zdefs?>/; 402 } 403 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line); 404 next; 405 } 406 # Look for relocations whose symbols can't be found. Note, we 407 # only print out the first 5 relocations for any file as this 408 # output can be excessive. 409 if ($Sym && ($Line =~ /symbol not found/)) { 410 # Determine if this file is allowed undefined 411 # references. 412 if (($Sym == 5) && defined($EXRE_undef_ref) && 413 ($RelPath =~ $EXRE_undef_ref)) { 414 $Sym = 0; 415 next; 416 } 417 if ($Sym-- == 1) { 418 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 419 "continued ...") if !$opt{o}; 420 next; 421 } 422 # Just print the symbol name. 423 $Line =~ s/$/\t<no -zdefs?>/; 424 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line); 425 next; 426 } 427 # Look for any unused search paths. 428 if ($Line =~ /unused search path=/) { 429 # Note, skip this comparison for __GNUC builds, as the 430 # gnu compilers insert numerous unused search paths. 431 if ($Gnuc == 1) { 432 next; 433 } 434 next if defined($EXRE_unused_rpath) && 435 ($Line =~ $EXRE_unused_rpath); 436 437 if ($Secure) { 438 $Line =~ s!$Tmpdir/!!; 439 } 440 $Line =~ s/^[ \t]*(.*)/\t$1\t<remove search path?>/; 441 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line); 442 next; 443 } 444 # Look for unreferenced dependencies. Note, if any unreferenced 445 # objects are ignored, then set $UnDep so as to suppress any 446 # associated unused-object messages. 447 if ($Line =~ /unreferenced object=/) { 448 if (defined($EXRE_unref_obj) && 449 ($Line =~ $EXRE_unref_obj)) { 450 $UnDep = 0; 451 next; 452 } 453 if ($Secure) { 454 $Line =~ s!$Tmpdir/!!; 455 } 456 $Line =~ s/^[ \t]*(.*)/$1\t<remove lib or -zignore?>/; 457 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line); 458 next; 459 } 460 # Look for any unused dependencies. 461 if ($UnDep && ($Line =~ /unused/)) { 462 # Skip if object is allowed to have unused dependencies 463 next if defined($EXRE_unused_deps) && 464 ($RelPath =~ $EXRE_unused_deps); 465 466 # Skip if dependency is always allowed to be unused 467 next if defined($EXRE_unused_obj) && 468 ($Line =~ $EXRE_unused_obj); 469 470 $Line =~ s!$Tmpdir/!! if $Secure; 471 $Line =~ s/^[ \t]*(.*)/$1\t<remove lib or -zignore?>/; 472 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line); 473 next; 474 } 475 } 476 477 # Reuse the elfdump(1) data to investigate additional dynamic linking 478 # information. 479 480 $Sun = $Relsz = $Pltsz = $Dyn = $Stab = $SymSort = 0; 481 $Tex = $Strip = 1; 482 $HasDirectBinding = 0; 483 484 $Header = 'None'; 485ELF: foreach my $Line (@Elf) { 486 # We're only interested in the section headers and the dynamic 487 # section. 488 if ($Line =~ /^Section Header/) { 489 $Header = 'Shdr'; 490 491 if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) { 492 # This object has a combined relocation section. 493 $Sun = 1; 494 495 } elsif (($Stab == 0) && ($Line =~ /\.stab/)) { 496 # This object contain .stabs sections 497 $Stab = 1; 498 } elsif (($SymSort == 0) && 499 ($Line =~ /\.SUNW_dyn(sym)|(tls)sort/)) { 500 # This object contains a symbol sort section 501 $SymSort = 1; 502 } 503 504 if (($Strip == 1) && ($Line =~ /\.symtab/)) { 505 # This object contains a complete symbol table. 506 $Strip = 0; 507 } 508 next; 509 510 } elsif ($Line =~ /^Dynamic Section/) { 511 $Header = 'Dyn'; 512 next; 513 } elsif ($Line =~ /^Syminfo Section/) { 514 $Header = 'Syminfo'; 515 next; 516 } elsif (($Header ne 'Dyn') && ($Header ne 'Syminfo')) { 517 next; 518 } 519 520 # Look into the Syminfo section. 521 # Does this object have at least one Directly Bound symbol? 522 if (($Header eq 'Syminfo')) { 523 my(@Symword); 524 525 if ($HasDirectBinding == 1) { 526 next; 527 } 528 529 @Symword = split(' ', $Line); 530 531 if (!defined($Symword[1])) { 532 next; 533 } 534 if ($Symword[1] =~ /B/) { 535 $HasDirectBinding = 1; 536 } 537 next; 538 } 539 540 # Does this object contain text relocations. 541 if ($Tex && ($Line =~ /TEXTREL/)) { 542 # Determine if this file is allowed text relocations. 543 if (defined($EXRE_textrel) && 544 ($RelPath =~ $EXRE_textrel)) { 545 $Tex = 0; 546 next ELF; 547 } 548 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 549 "TEXTREL .dynamic tag\t\t\t<no -Kpic?>"); 550 $Tex = 0; 551 next; 552 } 553 554 # Does this file have any relocation sections (there are a few 555 # psr libraries with no relocations at all, thus a .SUNW_reloc 556 # section won't exist either). 557 if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) { 558 $Relsz = hex((split(' ', $Line))[2]); 559 next; 560 } 561 562 # Does this file have any plt relocations. If the plt size is 563 # equivalent to the total relocation size then we don't have 564 # any relocations suitable for combining into a .SUNW_reloc 565 # section. 566 if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) { 567 $Pltsz = hex((split(' ', $Line))[2]); 568 next; 569 } 570 571 # Does this object have any dependencies. 572 if ($Line =~ /NEEDED/) { 573 my($Need) = (split(' ', $Line))[3]; 574 575 if (defined($EXRE_olddep) && ($Need =~ $EXRE_olddep)) { 576 # Catch any old (unnecessary) dependencies. 577 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 578 "NEEDED=$Need\t<dependency no longer necessary>"); 579 } elsif ($opt{i}) { 580 # Under the -i (information) option print out 581 # any useful dynamic entries. 582 onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath, 583 "NEEDED=$Need"); 584 } 585 next; 586 } 587 588 # Is this object built with -B direct flag on? 589 if ($Line =~ / DIRECT /) { 590 $HasDirectBinding = 1; 591 } 592 593 # Does this object specify a runpath. 594 if ($opt{i} && ($Line =~ /RPATH/)) { 595 my($Rpath) = (split(' ', $Line))[3]; 596 onbld_elfmod::OutMsg($InfoFH, $InfoTtl, 597 $RelPath, "RPATH=$Rpath"); 598 next; 599 } 600 } 601 602 # A shared object, that contains non-plt relocations, should have a 603 # combined relocation section indicating it was built with -z combreloc. 604 if (($Type eq 'DYN') && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) { 605 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 606 "SUNW_reloc section missing\t\t<no -zcombreloc?>"); 607 } 608 609 # No objects released to a customer should have any .stabs sections 610 # remaining, they should be stripped. 611 if ($opt{s} && $Stab) { 612 goto DONESTAB if defined($EXRE_stab) && ($RelPath =~ $EXRE_stab); 613 614 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 615 "debugging sections should be deleted\t<no strip -x?>"); 616 } 617 618 # Identify an object that is not built with either -B direct or 619 # -z direct. 620 goto DONESTAB 621 if (defined($EXRE_nodirect) && ($RelPath =~ $EXRE_nodirect)); 622 623 if ($Relsz && ($HasDirectBinding == 0)) { 624 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 625 "object has no direct bindings\t<no -B direct or -z direct?>"); 626 } 627 628DONESTAB: 629 630 # All objects should have a full symbol table to provide complete 631 # debugging stack traces. 632 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 633 "symbol table should not be stripped\t<remove -s?>") if $Strip; 634 635 # If there are symbol sort sections in this object, report on 636 # any that have duplicate addresses. 637 ProcSymSort($FullPath, $RelPath) if $SymSort; 638 639 # If -v was specified, and the object has a version definition 640 # section, generate output showing each public symbol and the 641 # version it belongs to. 642 ProcVerdef($FullPath, $RelPath) 643 if ($Verdef eq 'VERDEF') && $opt{v}; 644} 645 646 647## ProcSymSortOutMsg(RelPath, secname, addr, names...) 648# 649# Call onbld_elfmod::OutMsg for a duplicate address error in a symbol sort 650# section 651# 652sub ProcSymSortOutMsg { 653 my($RelPath, $secname, $addr, @names) = @_; 654 655 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 656 "$secname: duplicate $addr: ". join(', ', @names)); 657} 658 659 660## ProcSymSort(FullPath, RelPath) 661# 662# Examine the symbol sort sections for the given object and report 663# on any duplicate addresses found. Ideally, mapfile directives 664# should be used when building objects that have multiple symbols 665# with the same address so that only one of them appears in the sort 666# section. This saves space, reduces user confusion, and ensures that 667# libproc and debuggers always display public names instead of symbols 668# that are merely implementation details. 669# 670sub ProcSymSort { 671 672 my($FullPath, $RelPath) = @_; 673 674 # If this object is exempt from checking, return quietly 675 return if defined($EXRE_nosymsort) && ($FullPath =~ $EXRE_nosymsort); 676 677 678 open(SORT, "elfdump -S $FullPath|") || 679 die "$Prog: Unable to execute elfdump (symbol sort sections)\n"; 680 681 my $line; 682 my $last_addr; 683 my @dups = (); 684 my $secname; 685 while ($line = <SORT>) { 686 chomp $line; 687 688 next if ($line eq ''); 689 690 # If this is a header line, pick up the section name 691 if ($line =~ /^Symbol Sort Section:\s+([^\s]+)\s+/) { 692 $secname = $1; 693 694 # Every new section is followed by a column header line 695 $line = <SORT>; # Toss header line 696 697 # Flush anything left from previous section 698 ProcSymSortOutMsg($RelPath, $secname, $last_addr, @dups) 699 if (scalar(@dups) > 1); 700 701 # Reset variables for new sort section 702 $last_addr = ''; 703 @dups = (); 704 705 next; 706 } 707 708 # Process symbol line 709 my @fields = split /\s+/, $line; 710 my $new_addr = $fields[2]; 711 my $new_type = $fields[8]; 712 my $new_name = $fields[9]; 713 714 if ($new_type eq 'UNDEF') { 715 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, 716 "$secname: unexpected UNDEF symbol " . 717 "(link-editor error): $new_name"); 718 next; 719 } 720 721 if ($new_addr eq $last_addr) { 722 push @dups, $new_name; 723 } else { 724 ProcSymSortOutMsg($RelPath, $secname, 725 $last_addr, @dups) if (scalar(@dups) > 1); 726 @dups = ( $new_name ); 727 $last_addr = $new_addr; 728 } 729 } 730 731 ProcSymSortOutMsg($RelPath, $secname, $last_addr, @dups) 732 if (scalar(@dups) > 1); 733 734 close SORT; 735} 736 737 738## ProcVerdef(FullPath, RelPath) 739# 740# Examine the version definition section for the given object and report 741# each public symbol along with the version it belongs to. 742# 743sub ProcVerdef { 744 745 my($FullPath, $RelPath) = @_; 746 my $line; 747 my $cur_ver = ''; 748 my $tab = $opt{o} ? '' : "\t"; 749 750 # pvs -dov provides information about the versioning hierarchy 751 # in the file. Lines are of the format: 752 # path - version[XXX]; 753 # where [XXX] indicates optional information, such as flags 754 # or inherited versions. 755 # 756 # Private versions are allowed to change freely, so ignore them. 757 open(PVS, "pvs -dov $FullPath|") || 758 die "$Prog: Unable to execute pvs (version definition section)\n"; 759 760 while ($line = <PVS>) { 761 chomp $line; 762 763 if ($line =~ /^[^\s]+\s+-\s+([^;]+)/) { 764 my $ver = $1; 765 766 next if $ver =~ /private/i; 767 onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath, 768 "${tab}VERDEF=$ver"); 769 } 770 } 771 close PVS; 772 773 # pvs -dos lists the symbols assigned to each version definition. 774 # Lines are of the format: 775 # path - version: symbol; 776 # path - version: symbol (size); 777 # where the (size) is added to data items, but not for functions. 778 # We strip off the size, if present. 779 780 open(PVS, "pvs -dos $FullPath|") || 781 die "$Prog: Unable to execute pvs (version definition section)\n"; 782 while ($line = <PVS>) { 783 chomp $line; 784 if ($line =~ /^[^\s]+\s+-\s+([^:]+):\s*([^\s;]+)/) { 785 my $ver = $1; 786 my $sym = $2; 787 788 next if $ver =~ /private/i; 789 790 if ($opt{o}) { 791 onbld_elfmod::OutMsg($InfoFH, $InfoTtl, $RelPath, 792 "VERSION=$ver, SYMBOL=$sym"); 793 } else { 794 if ($cur_ver ne $ver) { 795 onbld_elfmod::OutMsg($InfoFH, $InfoTtl, 796 $RelPath, "VERSION=$ver"); 797 $cur_ver = $ver; 798 } 799 onbld_elfmod::OutMsg($InfoFH, $InfoTtl, 800 $RelPath, "SYMBOL=$sym"); 801 } 802 } 803 } 804 805 close PVS; 806} 807 808 809## OpenFindElf(file, FileHandleRef, LineNumRef) 810# 811# Open file in 'find_elf -r' format, and return the value of 812# the opening PREFIX line. 813# 814# entry: 815# file - file, or find_elf child process, to open 816# FileHandleRef - Reference to file handle to open 817# LineNumRef - Reference to integer to increment as lines are input 818# 819# exit: 820# This routine issues a fatal error and does not return on error. 821# Otherwise, the value of PREFIX is returned. 822# 823sub OpenFindElf { 824 my ($file, $fh, $LineNum) = @_; 825 my $line; 826 my $prefix; 827 828 open($fh, $file) || die "$Prog: Unable to open: $file"; 829 $$LineNum = 0; 830 831 # This script requires relative paths as created by 'find_elf -r'. 832 # When this is done, the first non-comment line will always 833 # be PREFIX. Obtain that line, or issue a fatal error. 834 while ($line = onbld_elfmod::GetLine($fh, $LineNum)) { 835 if ($line =~ /^PREFIX\s+(.*)$/i) { 836 $prefix = $1; 837 last; 838 } 839 840 die "$Prog: No PREFIX line seen on line $$LineNum: $file"; 841 } 842 843 $prefix; 844} 845 846 847## ProcFindElf(file) 848# 849# Open the specified file, which must be produced by "find_elf -r", 850# and process the files it describes. 851# 852sub ProcFindElf { 853 my $file = $_[0]; 854 my $line; 855 my $LineNum; 856 857 my $prefix = OpenFindElf($file, \*FIND_ELF, \$LineNum); 858 859 while ($line = onbld_elfmod::GetLine(\*FIND_ELF, \$LineNum)) { 860 next if !($line =~ /^OBJECT\s/i); 861 862 my ($item, $class, $type, $verdef, $obj) = 863 split(/\s+/, $line, 5); 864 865 ProcFile("$prefix/$obj", $obj, $class, $type, $verdef); 866 } 867 868 close FIND_ELF; 869} 870 871 872## AltObjectConfig(file) 873# 874# Recurse through a directory hierarchy looking for appropriate dependencies 875# to map from their standard system locations to the proto area via a crle 876# config file. 877# 878# entry: 879# file - File of ELF objects, in 'find_elf -r' format, to examine. 880# 881# exit: 882# Scripts are generated for the 32 and 64-bit cases to run crle 883# and create runtime configuration files that will establish 884# alternative dependency mappings for the objects identified. 885# 886# $Env - Set to environment variable definitions that will cause 887# the config files generated by this routine to be used 888# by ldd. 889# $Conf32, $Conf64 - Undefined, or set to the config files generated 890# by this routine. If defined, the caller is responsible for 891# unlinking the files before exiting. 892# 893sub AltObjectConfig { 894 my $file = $_[0]; 895 my ($Crle32, $Crle64); 896 my $line; 897 my $LineNum; 898 my $obj_path; 899 my $obj_active = 0; 900 my $obj_class; 901 902 my $prefix = OpenFindElf($file, \*FIND_ELF); 903 904LINE: 905 while ($line = onbld_elfmod::GetLine(\*FIND_ELF, \$LineNum)) { 906 ITEM: { 907 908 if ($line =~ /^OBJECT\s/i) { 909 my ($item, $class, $type, $verdef, $obj) = 910 split(/\s+/, $line, 5); 911 912 if ($type eq 'DYN') { 913 $obj_active = 1; 914 $obj_path = $obj; 915 $obj_class = $class; 916 } else { 917 # Only want sharable objects 918 $obj_active = 0; 919 } 920 last ITEM; 921 } 922 923 # We need to follow links to sharable objects so 924 # that any dependencies are expressed in all their 925 # available forms. We depend on ALIAS lines directly 926 # following the object they alias, so if we have 927 # a current object, this alias belongs to it. 928 if ($obj_active && ($line =~ /^ALIAS\s/i)) { 929 my ($item, $real_obj, $obj) = 930 split(/\s+/, $line, 3); 931 $obj_path = $obj; 932 last ITEM; 933 } 934 935 # Skip unrecognized item 936 next LINE; 937 } 938 939 next if !$obj_active; 940 941 my $full = "$prefix/$obj_path"; 942 943 next if defined($EXRE_nocrlealt) && 944 ($obj_path =~ $EXRE_nocrlealt); 945 946 my $Dir = $full; 947 $Dir =~ s/^(.*)\/.*$/$1/; 948 949 # Create a crle(1) script for the dependency we've found. 950 # We build separate scripts for the 32 and 64-bit cases. 951 # We create and initialize each script when we encounter 952 # the first object that needs it. 953 if ($obj_class == 32) { 954 if (!$Crle32) { 955 $Crle32 = "$Tmpdir/$Prog.crle32.$$"; 956 open(CRLE32, "> $Crle32") || 957 die "$Prog: open failed: $Crle32: $!"; 958 print CRLE32 "#!/bin/sh\ncrle \\\n"; 959 } 960 print CRLE32 "\t-o $Dir -a /$obj_path \\\n"; 961 } elsif ($Ena64) { 962 if (!$Crle64) { 963 $Crle64 = "$Tmpdir/$Prog.crle64.$$"; 964 open(CRLE64, "> $Crle64") || 965 die "$Prog: open failed: $Crle64: $!"; 966 print CRLE64 "#!/bin/sh\ncrle -64\\\n"; 967 } 968 print CRLE64 "\t-o $Dir -a /$obj_path \\\n"; 969 } 970 } 971 972 close FIND_ELF; 973 974 975 # Now that the config scripts are complete, use them to generate 976 # runtime linker config files. 977 if ($Crle64) { 978 $Conf64 = "$Tmpdir/$Prog.conf64.$$"; 979 print CRLE64 "\t-c $Conf64\n"; 980 981 chmod 0755, $Crle64; 982 close CRLE64; 983 984 undef $Conf64 if system($Crle64); 985 986 # Done with the script 987 unlink $Crle64; 988 } 989 if ($Crle32) { 990 $Conf32 = "$Tmpdir/$Prog.conf32.$$"; 991 print CRLE32 "\t-c $Conf32\n"; 992 993 chmod 0755, $Crle32; 994 close CRLE32; 995 996 undef $Conf32 if system($Crle32); 997 998 # Done with the script 999 unlink $Crle32; 1000 } 1001 1002 # Set $Env so that we will use the config files generated above 1003 # when we run ldd. 1004 if ($Crle64 && $Conf64 && $Crle32 && $Conf32) { 1005 $Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32"; 1006 } elsif ($Crle64 && $Conf64) { 1007 $Env = "-e LD_FLAGS=config_64=$Conf64"; 1008 } elsif ($Crle32 && $Conf32) { 1009 $Env = "-e LD_FLAGS=config_32=$Conf32"; 1010 } 1011} 1012 1013# ----------------------------------------------------------------------------- 1014 1015# This script relies on ldd returning output reflecting only the binary 1016# contents. But if LD_PRELOAD* environment variables are present, libraries 1017# named by them will also appear in the output, disrupting our analysis. 1018# So, before we get too far, scrub the environment. 1019 1020delete($ENV{LD_PRELOAD}); 1021delete($ENV{LD_PRELOAD_32}); 1022delete($ENV{LD_PRELOAD_64}); 1023 1024# Establish a program name for any error diagnostics. 1025chomp($Prog = `basename $0`); 1026 1027# The onbld_elfmod package is maintained in the same directory as this 1028# script, and is installed in ../lib/perl. Use the local one if present, 1029# and the installed one otherwise. 1030my $moddir = dirname($0); 1031$moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm"; 1032require "$moddir/onbld_elfmod.pm"; 1033 1034# Determine what machinery is available. 1035my $Mach = `uname -p`; 1036my$Isalist = `isalist`; 1037if ($Mach =~ /sparc/) { 1038 if ($Isalist =~ /sparcv9/) { 1039 $Ena64 = "ok"; 1040 } 1041} elsif ($Mach =~ /i386/) { 1042 if ($Isalist =~ /amd64/) { 1043 $Ena64 = "ok"; 1044 } 1045} 1046 1047# $Env is used with all calls to ldd. It is set by AltObjectConfig to 1048# cause an alternate object mapping runtime config file to be used. 1049$Env = ''; 1050 1051# Check that we have arguments. 1052if ((getopts('D:d:E:e:f:I:imosvw:', \%opt) == 0) || 1053 (!$opt{f} && ($#ARGV == -1))) { 1054 print "usage: $Prog [-imosv] [-D depfile | -d depdir] [-E errfile]\n"; 1055 print "\t\t[-e exfile] [-f listfile] [-I infofile] [-w outdir]\n"; 1056 print "\t\t[file | dir]...\n"; 1057 print "\n"; 1058 print "\t[-D depfile]\testablish dependencies from 'find_elf -r' file list\n"; 1059 print "\t[-d depdir]\testablish dependencies from under directory\n"; 1060 print "\t[-E errfile]\tdirect error output to file\n"; 1061 print "\t[-e exfile]\texceptions file\n"; 1062 print "\t[-f listfile]\tuse file list produced by find_elf -r\n"; 1063 print "\t[-I infofile]\tdirect informational output (-i, -v) to file\n"; 1064 print "\t[-i]\t\tproduce dynamic table entry information\n"; 1065 print "\t[-m]\t\tprocess mcs(1) comments\n"; 1066 print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n"; 1067 print "\t[-s]\t\tprocess .stab and .symtab entries\n"; 1068 print "\t[-v]\t\tprocess version definition entries\n"; 1069 print "\t[-w outdir]\tinterpret all files relative to given directory\n"; 1070 exit 1; 1071} 1072 1073die "$Prog: -D and -d options are mutually exclusive\n" if ($opt{D} && $opt{d}); 1074 1075$Tmpdir = "/tmp" if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir)); 1076 1077# Determine whether this is a __GNUC build. If so, unused search path 1078# processing is disabled. 1079$Gnuc = defined $ENV{__GNUC} ? 1 : 0; 1080 1081# If -w, change working directory to given location 1082!$opt{w} || chdir($opt{w}) || die "$Prog: can't cd to $opt{w}"; 1083 1084# Locate and process the exceptions file 1085onbld_elfmod::LoadExceptionsToEXRE('check_rtime'); 1086 1087# Is there a proto area available, either via the -d option, or because 1088# we are part of an activated workspace? 1089my $Proto; 1090if ($opt{d}) { 1091 # User specified dependency directory - make sure it exists. 1092 -d $opt{d} || die "$Prog: $opt{d} is not a directory\n"; 1093 $Proto = $opt{d}; 1094} elsif ($ENV{CODEMGR_WS}) { 1095 my $Root; 1096 1097 # Without a user specified dependency directory see if we're 1098 # part of a codemanager workspace and if a proto area exists. 1099 $Proto = $Root if ($Root = $ENV{ROOT}) && (-d $Root); 1100} 1101 1102# If we are basing this analysis off the sharable objects found in 1103# a proto area, then gather dependencies and construct an alternative 1104# dependency mapping via a crle(1) configuration file. 1105# 1106# To support alternative dependency mapping we'll need ldd(1)'s 1107# -e option. This is relatively new (s81_30), so make sure 1108# ldd(1) is capable before gathering any dependency information. 1109if ($opt{D} || $Proto) { 1110 if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) { 1111 print "ldd: does not support -e, unable to "; 1112 print "create alternative dependency mappingings.\n"; 1113 print "ldd: option added under 4390308 (s81_30).\n\n"; 1114 } else { 1115 # If -D was specified, it supplies a list of files in 1116 # 'find_elf -r' format, and can use it directly. Otherwise, 1117 # we will run find_elf as a child process to find the 1118 # sharable objects found under $Proto. 1119 AltObjectConfig($opt{D} ? $opt{D} : "find_elf -frs $Proto|"); 1120 } 1121} 1122 1123# To support unreferenced dependency detection we'll need ldd(1)'s -U 1124# option. This is relatively new (4638070), and if not available we 1125# can still fall back to -u. Even with this option, don't use -U with 1126# releases prior to 5.10 as the cleanup for -U use only got integrated 1127# into 5.10 under 4642023. Note, that nightly doesn't typically set a 1128# RELEASE from the standard <env> files. Users who wish to disable use 1129# of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file 1130# if using nightly, or otherwise establish it in their environment. 1131if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) { 1132 $LddNoU = 1; 1133} else { 1134 my($Release); 1135 1136 if (($Release = $ENV{RELEASE}) && (cmp_os_ver($Release, "<", "5.10"))) { 1137 $LddNoU = 1; 1138 } else { 1139 $LddNoU = 0; 1140 } 1141} 1142 1143# Set up variables used to handle output files: 1144# 1145# Error messages go to stdout unless -E is specified. $ErrFH is a 1146# file handle reference that points at the file handle where error messages 1147# are sent, and $ErrTtl is a reference that points at an integer used 1148# to count how many lines have been sent there. 1149# 1150# Informational messages go to stdout unless -I is specified. $InfoFH is a 1151# file handle reference that points at the file handle where info messages 1152# are sent, and $InfoTtl is a reference that points at an integer used 1153# to count how many lines have been sent there. 1154# 1155if ($opt{E}) { 1156 open(ERROR, ">$opt{E}") || die "$Prog: open failed: $opt{E}"; 1157 $ErrFH = \*ERROR; 1158} else { 1159 $ErrFH = \*STDOUT; 1160} 1161 1162if ($opt{I}) { 1163 open(INFO, ">$opt{I}") || die "$Prog: open failed: $opt{I}"; 1164 $InfoFH = \*INFO; 1165} else { 1166 $InfoFH = \*STDOUT; 1167} 1168my ($err_dev, $err_ino) = stat($ErrFH); 1169my ($info_dev, $info_ino) = stat($InfoFH); 1170$ErrTtl = \$OutCnt1; 1171$InfoTtl = (($err_dev == $info_dev) && ($err_ino == $info_ino)) ? 1172 \$OutCnt1 : \$OutCnt2; 1173 1174 1175# If we were given a list of objects in 'find_elf -r' format, then 1176# process it. 1177ProcFindElf($opt{f}) if $opt{f}; 1178 1179# Process each argument 1180foreach my $Arg (@ARGV) { 1181 # Run find_elf to find the files given by $Arg and process them 1182 ProcFindElf("find_elf -fr $Arg|"); 1183} 1184 1185# Cleanup output files 1186unlink $Conf64 if $Conf64; 1187unlink $Conf32 if $Conf32; 1188close ERROR if $opt{E}; 1189close INFO if $opt{I}; 1190 1191exit 0; 1192