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