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