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