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