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