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