1 #!/usr/perl5/bin/perl -w
2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22
23 #
24 # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
25 #
26
27 #
28 # Check ELF information.
29 #
30 # This script descends a directory hierarchy inspecting ELF dynamic executables
31 # and shared objects. The general theme is to verify that common Makefile rules
32 # have been used to build these objects. Typical failures occur when Makefile
33 # rules are re-invented rather than being inherited from "cmd/lib" Makefiles.
34 #
35 # As always, a number of components don't follow the rules, and these are
36 # excluded to reduce this scripts output.
37 #
38 # By default any file that has conditions that should be reported is first
39 # listed and then each condition follows. The -o (one-line) option produces a
40 # more terse output which is better for sorting/diffing with "nightly".
41 #
42 # NOTE: missing dependencies, symbols or versions are reported by running the
43 # file through ldd(1). As objects within a proto area are built to exist in a
44 # base system, standard use of ldd(1) will bind any objects to dependencies
45 # that exist in the base system. It is frequently the case that newer objects
46 # exist in the proto area that are required to satisfy other objects
47 # dependencies, and without using these newer objects an ldd(1) will produce
48 # misleading error messages. To compensate for this, the -D/-d options, or the
49 # existence of the CODEMSG_WS/ROOT environment variables, cause the creation of
50 # alternative dependency mappings via crle(1) configuration files that establish
51 # any proto shared objects as alternatives to their base system location. Thus
52 # ldd(1) can be executed against these configuration files so that objects in a
53 # proto area bind to their dependencies in the same proto area.
54
55
56 # Define all global variables (required for strict)
57 use vars qw($Prog $Env $Ena64 $Tmpdir);
58 use vars qw($LddNoU $Conf32 $Conf64);
59 use vars qw(%opt);
60 use vars qw($ErrFH $ErrTtl $InfoFH $InfoTtl $OutCnt1 $OutCnt2);
61
62 # An exception file is used to specify regular expressions to match
63 # objects. These directives specify special attributes of the object.
64 # The regular expressions are read from the file and compiled into the
65 # regular expression variables.
66 #
67 # The name of each regular expression variable is of the form
68 #
69 # $EXRE_xxx
70 #
71 # where xxx is the name of the exception in lower case. For example,
72 # the regular expression variable for EXEC_STACK is $EXRE_exec_stack.
73 #
74 # onbld_elfmod::LoadExceptionsToEXRE() depends on this naming convention
75 # to initialize the regular expression variables, and to detect invalid
76 # exception names.
77 #
78 # If a given exception is not used in the exception file, its regular
79 # expression variable will be undefined. Users of these variables must
80 # test the variable with defined() prior to use:
81 #
82 # defined($EXRE_exec_stack) && ($foo =~ $EXRE_exec_stack)
83 #
84 # or if the test is to make sure the item is not specified:
85 #
86 # !defined($EXRE_exec_stack) || ($foo !~ $EXRE_exec_stack)
87 #
88 # ----
89 #
90 # The exceptions are:
91 #
92 # EXEC_DATA
93 # Objects that are not required to have non-executable writable
94 # data segments.
95 #
96 # EXEC_STACK
97 # Objects that are not required to have a non-executable stack
98 #
99 # FORBIDDEN_DEP
100 # Objects allowed to link to 'forbidden' objects
101 #
102 # FORBIDDEN
103 # Objects to which nobody not excepted with FORBIDDEN_DEP may link
104 #
105 # NOCRLEALT
106 # Objects that should be skipped by AltObjectConfig() when building
107 # the crle script that maps objects to the proto area.
108 #
109 # NODIRECT
110 # Objects that are not required to use direct bindings
111 #
112 # NOSYMSORT
113 # Objects we should not check for duplicate addresses in
114 # the symbol sort sections.
115 #
116 # OLDDEP
117 # Objects that are no longer needed because their functionalty
118 # has migrated elsewhere. These are usually pure filters that
119 # point at libc.
120 #
121 # SKIP
122 # Files and directories that should be excluded from analysis.
123 #
124 # STAB
125 # Objects that are allowed to contain stab debugging sections
126 #
127 # TEXTREL
128 # Object for which relocations are allowed to the text segment
129 #
130 # UNDEF_REF
131 # Objects that are allowed undefined references
132 #
133 # UNREF_OBJ
134 # "unreferenced object=" ldd(1) diagnostics.
135 #
136 # UNUSED_DEPS
137 # Objects that are allowed to have unused dependencies
138 #
139 # UNUSED_OBJ
140 # Objects that are allowed to be unused dependencies
141 #
142 # UNUSED_RPATH
143 # Objects with unused runpaths
144 #
145
146 use vars qw($EXRE_exec_data $EXRE_exec_stack $EXRE_nocrlealt);
147 use vars qw($EXRE_nodirect $EXRE_nosymsort $EXRE_forbidden_dep $EXRE_forbidden);
148 use vars qw($EXRE_olddep $EXRE_skip $EXRE_stab $EXRE_textrel $EXRE_undef_ref);
149 use vars qw($EXRE_unref_obj $EXRE_unused_deps $EXRE_unused_obj);
150 use vars qw($EXRE_unused_rpath);
151
152 use strict;
153 use Getopt::Std;
154 use File::Basename;
155
156
157 # Reliably compare two OS revisions. Arguments are <ver1> <op> <ver2>.
158 # <op> is the string form of a normal numeric comparison operator.
159 sub cmp_os_ver {
160 my @ver1 = split(/\./, $_[0]);
161 my $op = $_[1];
162 my @ver2 = split(/\./, $_[2]);
163
164 push @ver2, ("0") x $#ver1 - $#ver2;
165 push @ver1, ("0") x $#ver2 - $#ver1;
166
167 my $diff = 0;
168 while (@ver1 || @ver2) {
169 if (($diff = shift(@ver1) - shift(@ver2)) != 0) {
170 last;
171 }
172 }
173 return (eval "$diff $op 0" ? 1 : 0);
174 }
175
176 ## ProcFile(FullPath, RelPath, File, Class, Type, Verdef)
177 #
178 # Determine whether this a ELF dynamic object and if so investigate its runtime
179 # attributes.
180 #
181 sub ProcFile {
182 my($FullPath, $RelPath, $Class, $Type, $Verdef) = @_;
183 my(@Elf, @Ldd, $Dyn, $Sym, $Stack);
184 my($Sun, $Relsz, $Pltsz, $Tex, $Stab, $Strip, $Lddopt, $SymSort);
185 my($Val, $Header, $IsX86, $RWX, $UnDep);
186 my($HasDirectBinding);
187
188 # Only look at executables and sharable objects
189 return if ($Type ne 'EXEC') && ($Type ne 'DYN');
190
191 # Ignore symbolic links
192 return if -l $FullPath;
193
194 # Is this an object or directory hierarchy we don't care about?
195 return if (defined($EXRE_skip) && ($RelPath =~ $EXRE_skip));
196
197 # Bail if we can't stat the file. Otherwise, note if it is SUID/SGID.
198 return if !stat($FullPath);
199 my $Secure = (-u _ || -g _) ? 1 : 0;
200
201 # Reset output message counts for new input file
202 $$ErrTtl = $$InfoTtl = 0;
203
204 @Ldd = 0;
205
206 # Determine whether we have access to inspect the file.
207 if (!(-r $FullPath)) {
208 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
209 "unable to inspect file: permission denied");
210 return;
211 }
212
213 # Determine whether we have a executable (static or dynamic) or a
214 # shared object.
215 @Elf = split(/\n/, `elfdump -epdcy $FullPath 2>&1`);
216
217 $Dyn = $Stack = $IsX86 = $RWX = 0;
218 $Header = 'None';
219 foreach my $Line (@Elf) {
220 # If we have an invalid file type (which we can tell from the
221 # first line), or we're processing an archive, bail.
222 if ($Header eq 'None') {
223 if (($Line =~ /invalid file/) ||
224 ($Line =~ /\Q$FullPath\E(.*):/)) {
225 return;
226 }
227 }
228
229 if ($Line =~ /^ELF Header/) {
230 $Header = 'Ehdr';
231 next;
232 }
233
234 if ($Line =~ /^Program Header/) {
235 $Header = 'Phdr';
236 $RWX = 0;
237 next;
238 }
239
240 if ($Line =~ /^Dynamic Section/) {
241 # A dynamic section indicates we're a dynamic object
242 # (this makes sure we don't check static executables).
243 $Dyn = 1;
244 next;
245 }
246
247 if (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) {
248 # If it's a X86 object, we need to enforce RW- data.
249 $IsX86 = 1 if $Line =~ /(EM_AMD64|EM_386)/;
250 next;
251 }
252
253 if (($Header eq 'Phdr') &&
254 ($Line =~ /\[ PF_X\s+PF_W\s+PF_R \]/)) {
255 # RWX segment seen.
256 $RWX = 1;
257 next;
258 }
259
260 if (($Header eq 'Phdr') &&
261 ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) {
262 # Seen an RWX PT_LOAD segment.
263 if (!defined($EXRE_exec_data) ||
264 ($RelPath !~ $EXRE_exec_data)) {
265 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
266 "application requires non-executable " .
267 "data\t<no -Mmapfile_noexdata?>");
268 }
269 next;
270 }
271
272 if (($Header eq 'Phdr') && ($Line =~ /\[ PT_SUNWSTACK \]/)) {
273 # This object defines a non-executable stack.
274 $Stack = 1;
275 next;
276 }
277 }
278
279 # Determine whether this ELF executable or shared object has a
280 # conforming mcs(1) comment section. If the correct $(POST_PROCESS)
281 # macros are used, only a 3 or 4 line .comment section should exist
282 # containing one or two "@(#)SunOS" identifying comments (one comment
283 # for a non-debug build, and two for a debug build). The results of
284 # the following split should be three or four lines, the last empty
285 # line being discarded by the split.
286 if ($opt{m}) {
287 my(@Mcs, $Con, $Dev);
288
289 @Mcs = split(/\n/, `mcs -p $FullPath 2>&1`);
290
291 $Con = $Dev = $Val = 0;
292 foreach my $Line (@Mcs) {
293 $Val++;
294
295 if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) {
296 $Con = 1;
297 last;
298 }
299 if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) {
300 $Dev = 1;
301 next;
302 }
303 if (($Dev == 0) && ($Val == 4)) {
304 $Con = 1;
305 last;
306 }
307 if (($Dev == 1) && ($Val == 5)) {
308 $Con = 1;
309 last;
310 }
311 }
312 if ($opt{m} && ($Con == 1)) {
313 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
314 "non-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>");
315 }
316 }
317
318 # Applications should contain a non-executable stack definition.
319 if (($Type eq 'EXEC') && ($Stack == 0) &&
320 (!defined($EXRE_exec_stack) || ($RelPath !~ $EXRE_exec_stack))) {
321 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
322 "non-executable stack required\t<no -Mmapfile_noexstk?>");
323 }
324
325 # Having caught any static executables in the mcs(1) check and non-
326 # executable stack definition check, continue with dynamic objects
327 # from now on.
328 if ($Dyn eq 0) {
329 return;
330 }
331
332 # Use ldd unless its a 64-bit object and we lack the hardware.
333 if (($Class == 32) || $Ena64) {
334 my $LDDFullPath = $FullPath;
335
336 if ($Secure) {
337 # The execution of a secure application over an nfs file
338 # system mounted nosuid will result in warning messages
339 # being sent to /var/adm/messages. As this type of
340 # environment can occur with root builds, move the file
341 # being investigated to a safe place first. In addition
342 # remove its secure permission so that it can be
343 # influenced by any alternative dependency mappings.
344
345 my $File = $RelPath;
346 $File =~ s!^.*/!!; # basename
347
348 my($TmpPath) = "$Tmpdir/$File";
349
350 system('cp', $LDDFullPath, $TmpPath);
351 chmod 0777, $TmpPath;
352 $LDDFullPath = $TmpPath;
353 }
354
355 # Use ldd(1) to determine the objects relocatability and use.
356 # By default look for all unreferenced dependencies. However,
357 # some objects have legitimate dependencies that they do not
358 # reference.
359 if ($LddNoU) {
360 $Lddopt = "-ru";
361 } else {
362 $Lddopt = "-rU";
363 }
364 @Ldd = split(/\n/, `ldd $Lddopt $Env $LDDFullPath 2>&1`);
365 if ($Secure) {
366 unlink $LDDFullPath;
367 }
368 }
369
370 $Val = 0;
371 $Sym = 5;
372 $UnDep = 1;
373
374 foreach my $Line (@Ldd) {
375
376 if ($Val == 0) {
377 $Val = 1;
378 # Make sure ldd(1) worked. One possible failure is that
379 # this is an old ldd(1) prior to -e addition (4390308).
380 if ($Line =~ /usage:/) {
381 $Line =~ s/$/\t<old ldd(1)?>/;
382 onbld_elfmod::OutMsg($ErrFH, $ErrTtl,
383 $RelPath, $Line);
384 last;
385 } elsif ($Line =~ /execution failed/) {
386 onbld_elfmod::OutMsg($ErrFH, $ErrTtl,
387 $RelPath, $Line);
388 last;
389 }
390
391 # It's possible this binary can't be executed, ie. we've
392 # found a sparc binary while running on an intel system,
393 # or a sparcv9 binary on a sparcv7/8 system.
394 if ($Line =~ /wrong class/) {
395 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
396 "has wrong class or data encoding");
397 next;
398 }
399
400 # Historically, ldd(1) likes executable objects to have
401 # their execute bit set.
402 if ($Line =~ /not executable/) {
403 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
404 "is not executable");
405 next;
406 }
407 }
408
409 # Look for "file" or "versions" that aren't found. Note that
410 # these lines will occur before we find any symbol referencing
411 # errors.
412 if (($Sym == 5) && ($Line =~ /not found\)/)) {
413 if ($Line =~ /file not found\)/) {
414 $Line =~ s/$/\t<no -zdefs?>/;
415 }
416 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
417 next;
418 }
419 # Look for relocations whose symbols can't be found. Note, we
420 # only print out the first 5 relocations for any file as this
421 # output can be excessive.
422 if ($Sym && ($Line =~ /symbol not found/)) {
423 # Determine if this file is allowed undefined
424 # references.
425 if (($Sym == 5) && defined($EXRE_undef_ref) &&
426 ($RelPath =~ $EXRE_undef_ref)) {
427 $Sym = 0;
428 next;
429 }
430 if ($Sym-- == 1) {
431 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
432 "continued ...") if !$opt{o};
433 next;
434 }
435 # Just print the symbol name.
436 $Line =~ s/$/\t<no -zdefs?>/;
437 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
438 next;
439 }
440 # Look for any unused search paths.
441 if ($Line =~ /unused search path=/) {
442 next if defined($EXRE_unused_rpath) &&
443 ($Line =~ $EXRE_unused_rpath);
444
445 if ($Secure) {
446 $Line =~ s!$Tmpdir/!!;
447 }
448 $Line =~ s/^[ \t]*(.*)/\t$1\t<remove search path?>/;
449 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
450 next;
451 }
452
453 # Look for unreferenced dependencies. Note, if any unreferenced
454 # objects are ignored, then set $UnDep so as to suppress any
455 # associated unused-object messages.
456 if ($Line =~ /unreferenced object=/) {
457 if (defined($EXRE_unref_obj) &&
458 ($Line =~ $EXRE_unref_obj)) {
459 $UnDep = 0;
460 next;
461 }
462 if ($Secure) {
463 $Line =~ s!$Tmpdir/!!;
464 }
465 $Line =~ s/^[ \t]*(.*)/$1\t<remove lib or -zignore?>/;
466 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
467 next;
468 }
469 # Look for any unused dependencies.
470 if ($UnDep && ($Line =~ /unused/)) {
471 # Skip if object is allowed to have unused dependencies
472 next if defined($EXRE_unused_deps) &&
473 ($RelPath =~ $EXRE_unused_deps);
474
475 # Skip if dependency is always allowed to be unused
476 next if defined($EXRE_unused_obj) &&
477 ($Line =~ $EXRE_unused_obj);
478
479 $Line =~ s!$Tmpdir/!! if $Secure;
480 $Line =~ s/^[ \t]*(.*)/$1\t<remove lib or -zignore?>/;
481 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath, $Line);
482 next;
483 }
484 }
485
486 # Reuse the elfdump(1) data to investigate additional dynamic linking
487 # information.
488
489 $Sun = $Relsz = $Pltsz = $Dyn = $Stab = $SymSort = 0;
490 $Tex = $Strip = 1;
491 $HasDirectBinding = 0;
492
493 $Header = 'None';
494 ELF: foreach my $Line (@Elf) {
495 # We're only interested in the section headers and the dynamic
496 # section.
497 if ($Line =~ /^Section Header/) {
498 $Header = 'Shdr';
499
500 if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) {
501 # This object has a combined relocation section.
502 $Sun = 1;
503
504 } elsif (($Stab == 0) && ($Line =~ /\.stab/)) {
505 # This object contain .stabs sections
506 $Stab = 1;
507 } elsif (($SymSort == 0) &&
508 ($Line =~ /\.SUNW_dyn(sym)|(tls)sort/)) {
509 # This object contains a symbol sort section
510 $SymSort = 1;
511 }
512
513 if (($Strip == 1) && ($Line =~ /\.symtab/)) {
514 # This object contains a complete symbol table.
515 $Strip = 0;
516 }
517 next;
518
519 } elsif ($Line =~ /^Dynamic Section/) {
520 $Header = 'Dyn';
521 next;
522 } elsif ($Line =~ /^Syminfo Section/) {
523 $Header = 'Syminfo';
524 next;
525 } elsif (($Header ne 'Dyn') && ($Header ne 'Syminfo')) {
526 next;
527 }
528
529 # Look into the Syminfo section.
530 # Does this object have at least one Directly Bound symbol?
531 if (($Header eq 'Syminfo')) {
532 my(@Symword);
533
534 if ($HasDirectBinding == 1) {
535 next;
536 }
537
538 @Symword = split(' ', $Line);
539
540 if (!defined($Symword[1])) {
541 next;
542 }
543 if ($Symword[1] =~ /B/) {
544 $HasDirectBinding = 1;
545 }
546 next;
547 }
548
549 # Does this object contain text relocations.
550 if ($Tex && ($Line =~ /TEXTREL/)) {
551 # Determine if this file is allowed text relocations.
552 if (defined($EXRE_textrel) &&
553 ($RelPath =~ $EXRE_textrel)) {
554 $Tex = 0;
555 next ELF;
556 }
557 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
558 "TEXTREL .dynamic tag\t\t\t<no -Kpic?>");
559 $Tex = 0;
560 next;
561 }
562
563 # Does this file have any relocation sections (there are a few
564 # psr libraries with no relocations at all, thus a .SUNW_reloc
565 # section won't exist either).
566 if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) {
567 $Relsz = hex((split(' ', $Line))[2]);
568 next;
569 }
570
571 # Does this file have any plt relocations. If the plt size is
572 # equivalent to the total relocation size then we don't have
573 # any relocations suitable for combining into a .SUNW_reloc
574 # section.
575 if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) {
576 $Pltsz = hex((split(' ', $Line))[2]);
577 next;
578 }
579
580 # Does this object have any dependencies.
581 if ($Line =~ /NEEDED/) {
582 my($Need) = (split(' ', $Line))[3];
583
584 if (defined($EXRE_olddep) && ($Need =~ $EXRE_olddep)) {
585 # Catch any old (unnecessary) dependencies.
586 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
587 "NEEDED=$Need\t<dependency no " .
588 "longer necessary>");
589 } elsif (defined($EXRE_forbidden) &&
590 ($Need =~ $EXRE_forbidden)) {
591 next if defined($EXRE_forbidden_dep) &&
592 ($FullPath =~ $EXRE_forbidden_dep);
593
594 onbld_elfmod::OutMsg($ErrFH, $ErrTtl, $RelPath,
595 "NEEDED=$Need\t<forbidden dependency, " .
596 "missing -nodefaultlibs?>");
597 next;
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
647 DONESTAB:
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 #
671 sub 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 #
689 sub 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 #
762 sub 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 #
842 sub 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 #
871 sub 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 #
912 sub 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
923 LINE:
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
1039 delete($ENV{LD_PRELOAD});
1040 delete($ENV{LD_PRELOAD_32});
1041 delete($ENV{LD_PRELOAD_64});
1042
1043 # Establish a program name for any error diagnostics.
1044 chomp($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.
1049 my $moddir = dirname($0);
1050 $moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm";
1051 require "$moddir/onbld_elfmod.pm";
1052
1053 # Determine what machinery is available.
1054 my $Mach = `uname -p`;
1055 my$Isalist = `isalist`;
1056 if ($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.
1071 if ((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
1092 die "$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
1100 onbld_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?
1104 my $Proto;
1105 if ($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.
1124 if ($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.
1146 if (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 #
1170 if ($opt{E}) {
1171 open(ERROR, ">$opt{E}") || die "$Prog: open failed: $opt{E}";
1172 $ErrFH = \*ERROR;
1173 } else {
1174 $ErrFH = \*STDOUT;
1175 }
1176
1177 if ($opt{I}) {
1178 open(INFO, ">$opt{I}") || die "$Prog: open failed: $opt{I}";
1179 $InfoFH = \*INFO;
1180 } else {
1181 $InfoFH = \*STDOUT;
1182 }
1183 my ($err_dev, $err_ino) = stat($ErrFH);
1184 my ($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.
1192 ProcFindElf($opt{f}) if $opt{f};
1193
1194 # Process each argument
1195 foreach 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
1201 unlink $Conf64 if $Conf64;
1202 unlink $Conf32 if $Conf32;
1203 close ERROR if $opt{E};
1204 close INFO if $opt{I};
1205
1206 exit 0;