xref: /titanic_41/usr/src/tools/scripts/check_rtime.pl (revision d60099826db8cad80752050dc6430999f467c5e5)
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 2008 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27# ident	"%Z%%M%	%I%	%E% SMI"
28#
29
30#
31# Check ELF information.
32#
33# This script descends a directory hierarchy inspecting ELF dynamic executables
34# and shared objects.  The general theme is to verify that common Makefile rules
35# have been used to build these objects.  Typical failures occur when Makefile
36# rules are re-invented rather than being inherited from "cmd/lib" Makefiles.
37#
38# As always, a number of components don't follow the rules, and these are
39# excluded to reduce this scripts output.  Pathnames used for this exclusion
40# assume this script is being run over a "proto" area.  The -a (all) option
41# skips any exclusions.
42#
43# By default any file that has conditions that should be reported is first
44# listed and then each condition follows.  The -o (one-line) option produces a
45# more terse output which is better for sorting/diffing with "nightly".
46#
47# NOTE: missing dependencies, symbols or versions are reported by running the
48# file through ldd(1).  As objects within a proto area are built to exist in a
49# base system, standard use of ldd(1) will bind any objects to dependencies
50# that exist in the base system.  It is frequently the case that newer objects
51# exist in the proto area that are required to satisfy other objects
52# dependencies, and without using these newer objects an ldd(1) will produce
53# misleading error messages.  To compensate for this, the -d option (or the
54# existence of the CODEMSG_WS/ROOT environment variables) cause the creation of
55# alternative dependency mappings via crle(1) configuration files that establish
56# any proto shared objects as alternatives to their base system location.  Thus
57# ldd(1) can be executed against these configuration files so that objects in a
58# proto area bind to their dependencies in the same proto area.
59
60
61# Define all global variables (required for strict)
62use vars  qw($SkipDirs $SkipFiles $SkipTextrelFiles $SkipDirectBindFiles);
63use vars  qw($SkipUndefFiles $SkipUnusedDirs);
64use vars  qw($SkipStabFiles $SkipNoExStkFiles $SkipCrleConf);
65use vars  qw($SkipUnusedSearchPath $SkipUnrefObject);
66use vars  qw($Prog $Mach $Isalist $Env $Ena64 $Tmpdir $Error);
67use vars  qw($UnusedPaths $LddNoU $Crle32 $Crle64 $Conf32 $Conf64);
68use vars  qw($SkipDirectBindDirs $SkipInterps $SkipSymSort $OldDeps %opt);
69
70use strict;
71
72
73# Define any directories we should skip completely.
74$SkipDirs = qr{
75	usr/lib/devfsadm |		# 4382889
76	usr/lib/libc |			# optimized libc
77	usr/lib/rcm |			# 4426119
78	usr/perl5 |			# alan's taking care of these :-)
79	usr/src				# no need to look at shipped source
80}x;
81
82# Define any files we should skip completely.
83$SkipFiles = qr{ ^(?:
84	lddstub |			# lddstub has no dependencies
85	geniconvtbl\.so |		# 4384329
86	libssagent\.so\.1 |		# 4328854
87	libpsvcplugin_psr\.so\.1 |	# 4385799
88	libpsvcpolicy_psr\.so\.1 |	#  "  "
89	libpsvcpolicy\.so\.1 |		#  "  "
90	picl_slm\.so |			#  "  "
91	mod_ipp\.so |			# Apache loadable module
92	fptest |	# USIII specific extns. cause ldd noise on USII bld. m/c
93	grub
94	)$
95}x;
96
97# Define any files that are allowed text relocations.
98$SkipTextrelFiles = qr{ ^(?:
99	unix |				# kernel models are non-pic
100	mdb				# relocations against __RTC (dbx)
101	)$
102}x;
103
104# Define any directories or files that are allowed to have no direct bound
105# symbols
106$SkipDirectBindDirs = qr{
107	usr/ucb
108}x;
109
110$SkipDirectBindFiles = qr{ ^(?:
111	unix |
112	sbcp |
113	libproc.so.1 |
114	libnisdb.so.2
115	)$
116}x;
117
118# Define any files that are allowed undefined references.
119
120$SkipUndefFiles = qr{ ^(?:
121	libsvm\.so\.1 |			# libspmicommon.so.1 lacking
122	libnisdb\.so\.2			# C++
123	)$
124}x;
125
126# Define any files that have unused dependencies.
127$SkipUnusedDirs = qr{
128	lib/picl/plugins/ |		# require devtree dependencies
129	/lib/libp			# profile libc makes libm an unused
130}x;					#	dependency of standard libc
131
132# Define any files that should contain debugging information.
133$SkipStabFiles = qr{ ^(?:
134	unix
135	)$
136}x;
137
138# Define any files that don't require a non-executable stack definition.
139$SkipNoExStkFiles = qr{ ^(?:
140	forth |
141	unix |
142	multiboot
143	)$
144}x;
145
146# Identify any files that should be skipped when building a crle(1)
147# configuration file.  As the hwcap libraries can be loop-back mounted onto
148# libc, these can confuse crle(1) because of their identical dev/inode.
149$SkipCrleConf = qr{
150	lib/libc/libc_hwcap
151}x;
152
153# Skip "unused search path=" ldd(1) diagnostics.
154$SkipUnusedSearchPath = qr{
155	/usr/lib/fs/autofs.*\ from\ .automountd |		# dlopen()
156	/etc/ppp/plugins.*\ from\ .*pppd |			# dlopen()
157	/usr/lib/inet/ppp.*\ from\ .*pppd |			# dlopen()
158	/usr/sfw/lib.*\ from\ .*libipsecutil.so.1 |		# dlopen()
159	/usr/platform/.*rsmlib.*\ from\ .*librsm.so.2 |		# dlopen()
160	\$ORIGIN.*\ from\ .*fcode.so |				# dlopen()
161	/usr/platform/.*\ from\ .*/usr/platform |		# picl
162	/usr/lib/picl/.*\ from\ .*/usr/platform |		# picl
163	/usr/platform/.*\ from\ .*/usr/lib/picl |		# picl
164	/usr/lib/smbsrv.*\ from\ .*libsmb\.so\.1 |		# future needs
165	/usr/lib/mps/secv1.*\ from\ .*libnss3\.so |		# non-OSNet
166	/usr/lib/mps.*\ from\ .*libnss3\.so |			# non-OSNet
167	/usr/sfw/lib.*\ from\ .*libdbus-1\.so\.3 |		# non-OSNet
168	/usr/sfw/lib.*\ from\ .*libdbus-glib-1\.so\.2 |		# non-OSNet
169	/usr/sfw/lib.*\ from\ .*libglib-2\.0\.so\.0 |		# non-OSNet
170	/usr/X11/lib.*\ from\ .*libglib-2\.0\.so\.0 |		# non-OSNet
171	/usr/sfw/lib.*\ from\ .*libgobject-2\.0\.so\.0 |	# non-OSNet
172	/usr/X11/lib.*\ from\ .*libgobject-2\.0\.so\.0 |	# non-OSNet
173	/usr/sfw/lib.*\ from\ .*libcrypto\.so\.0\.9\.8 |	# non-OSNet
174	/usr/sfw/lib.*\ from\ .*libnetsnmp\.so\.5 |		# non-OSNet
175	/usr/sfw/lib.*\ from\ .*libgcc_s\.so\.1			# non-OSNet
176}x;
177
178# Skip "unreferenced object=" ldd(1) diagnostics.
179$SkipUnrefObject = qr{
180	/libmapmalloc\.so\.1;\ unused\ dependency\ of |		# interposer
181	/lib.*\ of\ .*/lib/picl/plugins/ |			# picl
182	/lib.*\ of\ .*libcimapi\.so |				# non-OSNET
183	/lib.*\ of\ .*libjvm\.so |				# non-OSNET
184	/lib.*\ of\ .*libnetsnmp\.so\.5 |			# non-OSNET
185	/lib.*\ of\ .*libnetsnmpagent\.so\.5 |			# non-OSNET
186	/lib.*\ of\ .*libnetsnmpmibs\.so\.5 |			# non-OSNET
187	/lib.*\ of\ .*libnetsnmphelpers\.so\.5 |		# non-OSNET
188	/lib.*\ of\ .*libnspr4\.so |				# non-OSNET
189	/lib.*\ of\ .*libsoftokn3\.so |				# non-OSNET
190	/lib.*\ of\ .*libspmicommon\.so\.1 |			# non-OSNET
191	/lib.*\ of\ .*libspmocommon\.so\.1 |			# non-OSNET
192	/lib.*\ of\ .*libssl3\.so |				# non-OSNET
193	/lib.*\ of\ .*libxml2\.so\.2 |				# non-OSNET
194	/lib.*\ of\ .*libxslt\.so\.1				# non-OSNET
195}x;
196
197# Define any files that should only have unused (ldd -u) processing.
198$UnusedPaths = qr{
199	ucb/shutdown			# libucb interposes on libc and makes
200					# dependencies on libc seem unnecessary
201}x;
202
203# Define interpreters we should ignore.
204$SkipInterps = qr{
205	misc/krtld |
206	misc/amd64/krtld |
207	misc/sparcv9/krtld
208}x;
209
210# Catch libintl and libw, although ld(1) will bind to these and thus determine
211# they're needed, their content was moved into libc as of on297 build 7.
212# libthread and libpthread were completely moved into libc as of on10 build 53.
213# libdl was moved into libc as of on10 build 49.  librt and libaio were moved
214# into libc as of Nevada build 44.
215$OldDeps = qr{ ^(?:
216	libintl\.so\.1 |
217	libw\.so\.1 |
218	libthread\.so\.1 |
219	libpthread\.so\.1 |
220	libdl\.so\.1 |
221	librt\.so\.1 |
222	libaio\.so\.1
223	)$
224}x;
225
226# Files for which we skip checking of duplicate addresses in the
227# symbol sort sections. Such exceptions should be rare --- most code will
228# not have duplicate addresses, since it takes assember or a "#pragma weak"
229# to do such aliasing in C. C++ is different: The compiler generates aliases
230# for implementation reasons, and the mangled names used to encode argument
231# and return value types are difficult to handle well in mapfiles.
232# Furthermore, the Sun compiler and gcc use different and incompatible
233# name mangling conventions. Since ON must be buildable by either, we
234# would have to maintain two sets of mapfiles for each such object.
235# C++ use is rare in ON, so this is not worth pursuing.
236#
237$SkipSymSort = qr{ ^.*(?:
238	opt/SUNWdtrt/tst/common/pid/tst.weak2.exe |	# DTrace test
239	lib/amd64/libnsl\.so\.1 |			# C++
240	lib/sparcv9/libnsl\.so\.1 |			# C++
241	lib/sparcv9/libfru\.so\.1 |			# C++
242	usr/lib/sgml/nsgmls |				# C++
243	ld\.so\.1					# libc_pic.a user
244	)$
245}x;
246
247use Getopt::Std;
248
249# -----------------------------------------------------------------------------
250
251# Reliably compare two OS revisions.  Arguments are <ver1> <op> <ver2>.
252# <op> is the string form of a normal numeric comparison operator.
253sub cmp_os_ver {
254	my @ver1 = split(/\./, $_[0]);
255	my $op = $_[1];
256	my @ver2 = split(/\./, $_[2]);
257
258	push @ver2, ("0") x $#ver1 - $#ver2;
259	push @ver1, ("0") x $#ver2 - $#ver1;
260
261	my $diff = 0;
262	while (@ver1 || @ver2) {
263		if (($diff = shift(@ver1) - shift(@ver2)) != 0) {
264			last;
265		}
266	}
267	return (eval "$diff $op 0" ? 1 : 0);
268}
269
270# This script relies on ldd returning output reflecting only the binary
271# contents.  But if LD_PRELOAD* environment variables are present, libraries
272# named by them will also appear in the output, disrupting our analysis.
273# So, before we get too far, scrub the environment.
274
275delete($ENV{LD_PRELOAD});
276delete($ENV{LD_PRELOAD_32});
277delete($ENV{LD_PRELOAD_64});
278
279# Establish a program name for any error diagnostics.
280chomp($Prog = `basename $0`);
281
282# Determine what machinery is available.
283$Mach = `uname -p`;
284$Isalist = `isalist`;
285$Env = "";
286if ($Mach =~ /sparc/) {
287	if ($Isalist =~ /sparcv9/) {
288		$Ena64 = "ok";
289	}
290} elsif ($Mach =~ /i386/) {
291	if ($Isalist =~ /amd64/) {
292		$Ena64 = "ok";
293	}
294}
295
296# Check that we have arguments.
297if ((getopts('ad:imos', \%opt) == 0) || ($#ARGV == -1)) {
298	print "usage: $Prog [-a] [-d depdir] [-m] [-o] [-s] file | dir, ...\n";
299	print "\t[-a]\t\tprocess all files (ignore any exception lists)\n";
300	print "\t[-d dir]\testablish dependencies from under directory\n";
301	print "\t[-i]\t\tproduce dynamic table entry information\n";
302	print "\t[-m]\t\tprocess mcs(1) comments\n";
303	print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
304	print "\t[-s]\t\tprocess .stab and .symtab entries\n";
305	exit 1;
306} else {
307	my($Proto);
308
309	if ($opt{d}) {
310		# User specified dependency directory - make sure it exists.
311		if (! -d $opt{d}) {
312			print "$Prog: $opt{d} is not a directory\n";
313			exit 1;
314		}
315		$Proto = $opt{d};
316
317	} elsif ($ENV{CODEMGR_WS}) {
318		my($Root);
319
320		# Without a user specified dependency directory see if we're
321		# part of a codemanager workspace and if a proto area exists.
322		if (($Root = $ENV{ROOT}) && (-d $Root)) {
323			$Proto = $Root;
324		}
325	}
326
327	if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir)) {
328		$Tmpdir = "/tmp";
329	}
330
331	# Look for dependencies under $Proto.
332	if ($Proto) {
333		# To support alternative dependency mapping we'll need ldd(1)'s
334		# -e option.  This is relatively new (s81_30), so make sure
335		# ldd(1) is capable before gathering any dependency information.
336		if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) {
337			print "ldd: does not support -e, unable to ";
338			print "create alternative dependency mappingings.\n";
339			print "ldd: option added under 4390308 (s81_30).\n\n";
340		} else {
341			# Gather dependencies and construct a alternative
342			# dependency mapping via a crle(1) configuration file.
343			GetDeps($Proto, "/");
344			GenConf();
345		}
346	}
347
348	# To support unreferenced dependency detection we'll need ldd(1)'s -U
349	# option.  This is relatively new (4638070), and if not available we
350	# can still fall back to -u.  Even with this option, don't use -U with
351	# releases prior to 5.10 as the cleanup for -U use only got integrated
352	# into 5.10 under 4642023.  Note, that nightly doesn't typically set a
353	# RELEASE from the standard <env> files.  Users who wish to disable use
354	# of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file
355	# if using nightly, or otherwise establish it in their environment.
356	if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) {
357		$LddNoU = 1;
358	} else {
359		my($Release);
360
361		if (($Release = $ENV{RELEASE}) &&
362		    (cmp_os_ver($Release, "<", "5.10"))) {
363			$LddNoU = 1;
364		} else {
365			$LddNoU = 0;
366		}
367	}
368
369	# For each argument determine if we're dealing with a file or directory.
370	foreach my $Arg (@ARGV) {
371		# Ignore symbolic links.
372		if (-l $Arg) {
373			next;
374		}
375
376		if (!stat($Arg)) {
377			next;
378		}
379
380		# Process simple files.
381		if (-f _) {
382			my($RelPath) = $Arg;
383			my($File) = $Arg;
384			my($Secure) = 0;
385
386			$RelPath =~ s!^.*/!./!;
387			$File =~ s!^.*/!!;
388
389			if (-u _ || -g _) {
390				$Secure = 1;
391			}
392
393			ProcFile($Arg, $RelPath, $File, $Secure);
394			next;
395		}
396		# Process directories.
397		if (-d _) {
398			ProcDir($Arg, ".");
399			next;
400		}
401
402		print "$Arg is not a file or directory\n";
403		$Error = 1;
404	}
405
406	# Cleanup
407	CleanUp();
408}
409
410$Error = 0;
411
412# Clean up any temporary files.
413sub CleanUp {
414	if ($Crle64) {
415		unlink $Crle64;
416	}
417	if ($Conf64) {
418		unlink $Conf64;
419	}
420	if ($Crle32) {
421		unlink $Crle32;
422	}
423	if ($Conf32) {
424		unlink $Conf32;
425	}
426}
427
428# Create an output message, either a one-liner (under -o) or preceded by the
429# files relative pathname as a title.
430sub OutMsg {
431	my($Ttl, $Path, $Msg) = @_;
432
433	if ($opt{o}) {
434		$Msg =~ s/^[ \t]*//;
435		print "$Path: $Msg\n";
436	} else {
437		if ($Ttl eq 0) {
438			print "==== $Path ====\n";
439		}
440		print "$Msg\n";
441	}
442}
443
444# Determine whether this a ELF dynamic object and if so investigate its runtime
445# attributes.
446sub ProcFile {
447	my($FullPath, $RelPath, $File, $Secure) = @_;
448	my(@Elf, @Ldd, $Dyn, $Intp, $Dll, $Ttl, $Sym, $Interp, $Stack);
449	my($Sun, $Relsz, $Pltsz, $Tex, $Stab, $Strip, $Lddopt, $SymSort);
450	my($Val, $Header, $SkipLdd, $IsX86, $RWX, $UnDep);
451	my($HasDirectBinding);
452
453	# Ignore symbolic links.
454	if (-l $FullPath) {
455		return;
456	}
457
458	$Ttl = 0;
459	@Ldd = 0;
460
461	# Determine whether we have access to inspect the file.
462	if (!(-r $FullPath)) {
463		OutMsg($Ttl++, $RelPath,
464		    "\tunable to inspect file: permission denied");
465		return;
466	}
467
468	# Determine if this is a file we don't care about.
469	if (!$opt{a}) {
470		if ($File =~ $SkipFiles) {
471			return;
472		}
473	}
474
475	# Determine whether we have a executable (static or dynamic) or a
476	# shared object.
477	@Elf = split(/\n/, `elfdump -epdicy $FullPath 2>&1`);
478
479	$Dyn = $Intp = $Dll = $Stack = $IsX86 = $RWX = 0;
480	$Interp = 1;
481	$Header = 'None';
482	foreach my $Line (@Elf) {
483		# If we have an invalid file type (which we can tell from the
484		# first line), or we're processing an archive, bail.
485		if ($Header eq 'None') {
486			if (($Line =~ /invalid file/) ||
487			    ($Line =~ /$FullPath(.*):/)) {
488				return;
489			}
490		}
491
492		if ($Line =~ /^ELF Header/) {
493			$Header = 'Ehdr';
494
495		} elsif ($Line =~ /^Program Header/) {
496			$Header = 'Phdr';
497			$RWX = 0;
498
499		} elsif ($Line =~ /^Interpreter/) {
500			$Header = 'Intp';
501
502		} elsif ($Line =~ /^Dynamic Section/) {
503			# A dynamic section indicates we're a dynamic object
504			# (this makes sure we don't check static executables).
505			$Dyn = 1;
506
507		} elsif (($Header eq 'Ehdr') && ($Line =~ /e_type:/)) {
508			# The e_type field indicates whether this file is a
509			# shared object (ET_DYN) or an executable (ET_EXEC).
510			if ($Line =~ /ET_DYN/) {
511				$Dll = 1;
512			} elsif ($Line !~ /ET_EXEC/) {
513				return;
514			}
515		} elsif (($Header eq 'Ehdr') && ($Line =~ /ei_class:/)) {
516			# If we encounter a 64-bit object, but we're not running
517			# on a 64-bit system, suppress calling ldd(1).
518			if (($Line =~ /ELFCLASS64/) && !$Ena64) {
519				$SkipLdd = 1;
520			}
521		} elsif (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) {
522			# If it's a X86 object, we need to enforce RW- data.
523			if (($Line =~ /(EM_AMD64|EM_386)/)) {
524				$IsX86 = 1;
525			}
526		} elsif (($Header eq 'Phdr') &&
527		    ($Line =~ /\[ PF_X  PF_W  PF_R \]/)) {
528			# RWX segment seen.
529			$RWX = 1;
530
531		} elsif (($Header eq 'Phdr') &&
532		    ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) {
533			# Seen an RWX PT_LOAD segment.
534			if ($File !~ $SkipNoExStkFiles) {
535				OutMsg($Ttl++, $RelPath,
536				    "\tapplication requires non-executable " .
537				    "data\t<no -Mmapfile_noexdata?>");
538			}
539
540		} elsif (($Header eq 'Phdr') &&
541		    ($Line =~ /\[ PT_SUNWSTACK \]/)) {
542			# This object defines a non-executable stack.
543			$Stack = 1;
544
545		} elsif (($Header eq 'Intp') && !$opt{a} &&
546		    ($Line =~ $SkipInterps)) {
547			# This object defines an interpretor we should skip.
548			$Interp = 0;
549		}
550	}
551
552	# Determine whether this ELF executable or shared object has a
553	# conforming mcs(1) comment section.  If the correct $(POST_PROCESS)
554	# macros are used, only a 3 or 4 line .comment section should exist
555	# containing one or two "@(#)SunOS" identifying comments (one comment
556	# for a non-debug build, and two for a debug build). The results of
557	# the following split should be three or four lines, the last empty
558	# line being discarded by the split.
559	if ($opt{m}) {
560		my(@Mcs, $Con, $Dev);
561
562		@Mcs = split(/\n/, `mcs -p $FullPath 2>&1`);
563
564		$Con = $Dev = $Val = 0;
565		foreach my $Line (@Mcs) {
566			$Val++;
567
568			if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) {
569				$Con = 1;
570				last;
571			}
572			if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) {
573				$Dev = 1;
574				next;
575			}
576			if (($Dev == 0) && ($Val == 4)) {
577				$Con = 1;
578				last;
579			}
580			if (($Dev == 1) && ($Val == 5)) {
581				$Con = 1;
582				last;
583			}
584		}
585		if ($opt{m} && ($Con == 1)) {
586			OutMsg($Ttl++, $RelPath,
587			    "\tnon-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>");
588		}
589	}
590
591	# Applications should contain a non-executable stack definition.
592	if (($Dll == 0) && ($Stack == 0)) {
593		if (!$opt{a}) {
594			if ($File =~ $SkipNoExStkFiles) {
595				goto DYN;
596			}
597		}
598		OutMsg($Ttl++, $RelPath,
599		    "\tapplication requires non-executable stack\t<no -Mmapfile_noexstk?>");
600	}
601
602DYN:
603	# Having caught any static executables in the mcs(1) check and non-
604	# executable stack definition check, continue with dynamic objects
605	# from now on.
606	if ($Dyn eq 0) {
607		return;
608	}
609
610	# Only use ldd unless we've encountered an interpreter that should
611	# be skipped.
612	if (!$SkipLdd && $Interp) {
613		my $LDDFullPath = $FullPath;
614
615		if ($Secure) {
616			# The execution of a secure application over an nfs file
617			# system mounted nosuid will result in warning messages
618			# being sent to /var/adm/messages.  As this type of
619			# environment can occur with root builds, move the file
620			# being investigated to a safe place first.  In addition
621			# remove its secure permission so that it can be
622			# influenced by any alternative dependency mappings.
623
624			my($TmpPath) = "$Tmpdir/$File";
625
626			system('cp', $LDDFullPath, $TmpPath);
627			chmod 0777, $TmpPath;
628			$LDDFullPath = $TmpPath;
629		}
630
631		# Use ldd(1) to determine the objects relocatability and use.
632		# By default look for all unreferenced dependencies.  However,
633		# some objects have legitimate dependencies that they do not
634		# reference.
635		if ($LddNoU || ($RelPath =~ $UnusedPaths)) {
636			$Lddopt = "-ru";
637		} else {
638			$Lddopt = "-rU";
639		}
640		@Ldd = split(/\n/, `ldd $Lddopt $Env $LDDFullPath 2>&1`);
641		if ($Secure) {
642			unlink $LDDFullPath;
643		}
644	}
645
646	$Val = 0;
647	$Sym = 5;
648	$UnDep = 1;
649
650	foreach my $Line (@Ldd) {
651
652		if ($Val == 0) {
653			$Val = 1;
654			# Make sure ldd(1) worked.  One possible failure is that
655			# this is an old ldd(1) prior to -e addition (4390308).
656			if ($Line =~ /usage:/) {
657				$Line =~ s/$/\t<old ldd(1)?>/;
658				OutMsg($Ttl++, $RelPath, $Line);
659				last;
660			} elsif ($Line =~ /execution failed/) {
661				OutMsg($Ttl++, $RelPath, $Line);
662				last;
663			}
664
665			# It's possible this binary can't be executed, ie. we've
666			# found a sparc binary while running on an intel system,
667			# or a sparcv9 binary on a sparcv7/8 system.
668			if ($Line =~ /wrong class/) {
669				OutMsg($Ttl++, $RelPath,
670				    "\thas wrong class or data encoding");
671				next;
672			}
673
674			# Historically, ldd(1) likes executable objects to have
675			# their execute bit set.  Note that this test isn't
676			# applied unless the -a option is in effect, as any
677			# non-executable files are skipped by default to reduce
678			# the cost of running this script.
679			if ($Line =~ /not executable/) {
680				OutMsg($Ttl++, $RelPath,
681				    "\tis not executable");
682				next;
683			}
684		}
685
686		# Look for "file" or "versions" that aren't found.  Note that
687		# these lines will occur before we find any symbol referencing
688		# errors.
689		if (($Sym == 5) && ($Line =~ /not found\)/)) {
690			if ($Line =~ /file not found\)/) {
691				$Line =~ s/$/\t<no -zdefs?>/;
692			}
693			OutMsg($Ttl++, $RelPath, $Line);
694			next;
695		}
696		# Look for relocations whose symbols can't be found.  Note, we
697		# only print out the first 5 relocations for any file as this
698		# output can be excessive.
699		if ($Sym && ($Line =~ /symbol not found/)) {
700			# Determine if this file is allowed undefined
701			# references.
702			if ($Sym == 5) {
703				if (!$opt{a}) {
704					if ($File =~ $SkipUndefFiles) {
705						$Sym = 0;
706						next;
707					}
708				}
709			}
710			if ($Sym-- == 1) {
711				if (!$opt{o}) {
712					OutMsg($Ttl++, $RelPath,
713					    "\tcontinued ...");
714				}
715				next;
716			}
717			# Just print the symbol name.
718			$Line =~ s/$/\t<no -zdefs?>/;
719			OutMsg($Ttl++, $RelPath, $Line);
720			next;
721		}
722		# Look for any unused search paths.
723		if ($Line =~ /unused search path=/) {
724			if (!$opt{a}) {
725				if ($Line =~ $SkipUnusedSearchPath) {
726					next;
727				}
728			}
729			if ($Secure) {
730				$Line =~ s!$Tmpdir/!!;
731			}
732			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove search path?>/;
733			OutMsg($Ttl++, $RelPath, $Line);
734			next;
735		}
736		# Look for unreferenced dependencies.  Note, if any unreferenced
737		# objects are ignored, then set $UnDep so as to suppress any
738		# associated unused-object messages.
739		if ($Line =~ /unreferenced object=/) {
740			if (!$opt{a}) {
741				if ($Line =~ $SkipUnrefObject) {
742					$UnDep = 0;
743					next;
744				}
745			}
746			if ($Secure) {
747				$Line =~ s!$Tmpdir/!!;
748			}
749			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/;
750			OutMsg($Ttl++, $RelPath, $Line);
751			next;
752		}
753		# Look for any unused dependencies.
754		if ($UnDep && ($Line =~ /unused/)) {
755			if (!$opt{a}) {
756				if ($RelPath =~ $SkipUnusedDirs) {
757					$UnDep = 0;
758					next;
759				}
760			}
761			if ($Secure) {
762				$Line =~ s!$Tmpdir/!!;
763			}
764			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/;
765			OutMsg($Ttl++, $RelPath, $Line);
766			next;
767		}
768	}
769
770	# Reuse the elfdump(1) data to investigate additional dynamic linking
771	# information.
772
773	$Sun = $Relsz = $Pltsz = $Dyn = $Stab = $SymSort = 0;
774	$Tex = $Strip = 1;
775	$HasDirectBinding = 0;
776
777	$Header = 'None';
778ELF:	foreach my $Line (@Elf) {
779		# We're only interested in the section headers and the dynamic
780		# section.
781		if ($Line =~ /^Section Header/) {
782			$Header = 'Shdr';
783
784			if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) {
785				# This object has a combined relocation section.
786				$Sun = 1;
787
788			} elsif (($Stab == 0) && ($Line =~ /\.stab/)) {
789				# This object contain .stabs sections
790				$Stab = 1;
791			} elsif (($SymSort == 0) &&
792				 ($Line =~ /\.SUNW_dyn(sym)|(tls)sort/)) {
793				# This object contains a symbol sort section
794				$SymSort = 1;
795			}
796
797			if (($Strip == 1) && ($Line =~ /\.symtab/)) {
798				# This object contains a complete symbol table.
799				$Strip = 0;
800			}
801			next;
802
803		} elsif ($Line =~ /^Dynamic Section/) {
804			$Header = 'Dyn';
805			next;
806		} elsif ($Line =~ /^Syminfo Section/) {
807			$Header = 'Syminfo';
808			next;
809		} elsif (($Header ne 'Dyn') && ($Header ne 'Syminfo')) {
810			next;
811		}
812
813		# Look into the Syminfo section.
814		# Does this object have at least one Directly Bound symbol?
815		if (($Header eq 'Syminfo')) {
816			my(@Symword);
817
818			if ($HasDirectBinding == 1) {
819				next;
820			}
821
822			@Symword = split(' ', $Line);
823
824			if (!defined($Symword[1])) {
825				next;
826			}
827			if ($Symword[1] =~ /B/) {
828				$HasDirectBinding = 1;
829			}
830			next;
831		}
832
833		# Does this object contain text relocations.
834		if ($Tex && ($Line =~ /TEXTREL/)) {
835			# Determine if this file is allowed text relocations.
836			if (!$opt{a}) {
837				if ($File =~ $SkipTextrelFiles) {
838					$Tex = 0;
839					next ELF;
840				}
841			}
842			OutMsg($Ttl++, $RelPath,
843			    "\tTEXTREL .dynamic tag\t\t\t<no -Kpic?>");
844			$Tex = 0;
845			next;
846		}
847
848		# Does this file have any relocation sections (there are a few
849		# psr libraries with no relocations at all, thus a .SUNW_reloc
850		# section won't exist either).
851		if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) {
852			$Relsz = hex((split(' ', $Line))[2]);
853			next;
854		}
855
856		# Does this file have any plt relocations.  If the plt size is
857		# equivalent to the total relocation size then we don't have
858		# any relocations suitable for combining into a .SUNW_reloc
859		# section.
860		if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) {
861			$Pltsz = hex((split(' ', $Line))[2]);
862			next;
863		}
864
865		# Does this object have any dependencies.
866		if ($Line =~ /NEEDED/) {
867			my($Need) = (split(' ', $Line))[3];
868
869			if ($Need =~ $OldDeps) {
870				# Catch any old (unnecessary) dependencies.
871				OutMsg($Ttl++, $RelPath,
872				    "\tNEEDED=$Need\t<dependency no longer necessary>");
873			} elsif ($opt{i}) {
874				# Under the -i (information) option print out
875				# any useful dynamic entries.
876				OutMsg($Ttl++, $RelPath, "\tNEEDED=$Need");
877			}
878			next;
879		}
880
881		# Is this object built with -B direct flag on?
882		if ($Line =~ / DIRECT /) {
883			$HasDirectBinding = 1;
884		}
885
886		# Does this object specify a runpath.
887		if ($opt{i} && ($Line =~ /RPATH/)) {
888			my($Rpath) = (split(' ', $Line))[3];
889			OutMsg($Ttl++, $RelPath, "\tRPATH=$Rpath");
890			next;
891		}
892	}
893
894	# A shared object, that contains non-plt relocations, should have a
895	# combined relocation section indicating it was built with -z combreloc.
896	if ($Dll && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) {
897		OutMsg($Ttl++, $RelPath,
898		    "\tSUNW_reloc section missing\t\t<no -zcombreloc?>");
899	}
900
901	# No objects released to a customer should have any .stabs sections
902	# remaining, they should be stripped.
903	if ($opt{s} && $Stab) {
904		if (!$opt{a}) {
905			if ($File =~ $SkipStabFiles) {
906				goto DONESTAB;
907			}
908		}
909		OutMsg($Ttl++, $RelPath,
910		    "\tdebugging sections should be deleted\t<no strip -x?>");
911	}
912
913	# Identify an object that is not built with either -B direct or
914	# -z direct.
915	if (($RelPath =~ $SkipDirectBindDirs) ||
916	    ($File =~ $SkipDirectBindFiles)) {
917		goto DONESTAB;
918	}
919	if ($Relsz && ($HasDirectBinding == 0)) {
920		OutMsg($Ttl++, $RelPath,
921		    "\tobject has no direct bindings\t<no -B direct or -z direct?>");
922	}
923
924DONESTAB:
925
926	# All objects should have a full symbol table to provide complete
927	# debugging stack traces.
928	if ($Strip) {
929		OutMsg($Ttl++, $RelPath,
930		    "\tsymbol table should not be stripped\t<remove -s?>");
931	}
932
933	# If there are symbol sort sections in this object, report on
934	# any that have duplicate addresses.
935	ProcSymSort($FullPath, $RelPath, \$Ttl) if $SymSort;
936}
937
938
939## ProcSymSortOutMsg(RefTtl, RelPath, secname, addr, names...)
940#
941# Call OutMsg for a duplicate address error in a symbol sort
942# section
943#
944sub ProcSymSortOutMsg {
945	my($RefTtl, $RelPath, $secname, $addr, @names) = @_;
946
947	OutMsg($$RefTtl++, $RelPath,
948	    "$secname: duplicate $addr: ". join(', ', @names));
949}
950
951
952## ProcSymSort(FullPath, RelPath)
953#
954# Examine the symbol sort sections for the given object and report
955# on any duplicate addresses found.  Ideally, mapfile directives
956# should be used when building objects that have multiple symbols
957# with the same address so that only one of them appears in the sort
958# section. This saves space, reduces user confusion, and ensures that
959# libproc and debuggers always display public names instead of symbols
960# that are merely implementation details.
961#
962sub ProcSymSort {
963
964	my($FullPath, $RelPath, $RefTtl) = @_;
965
966	# If this object is exempt from checking, return quietly
967	return if ($FullPath =~ $SkipSymSort);
968
969
970	open(SORT, "elfdump -S $FullPath|") ||
971	    die "$Prog: Unable to execute elfdump (symbol sort sections)\n";
972
973	my $line;
974	my $last_addr;
975	my @dups = ();
976	my $secname;
977	while ($line = <SORT>) {
978		chomp $line;
979
980		next if ($line eq '');
981
982		# If this is a header line, pick up the section name
983		if ($line =~ /^Symbol Sort Section:\s+([^\s]+)\s+/) {
984			$secname = $1;
985
986			# Every new section is followed by a column header line
987			$line = <SORT>;		# Toss header line
988
989			# Flush anything left from previous section
990			ProcSymSortOutMsg($RefTtl, $RelPath, $secname,
991			    $last_addr, @dups) if (scalar(@dups) > 1);
992
993			# Reset variables for new sort section
994			$last_addr = '';
995			@dups = ();
996
997			next;
998		}
999
1000		# Process symbol line
1001		my @fields = split /\s+/, $line;
1002		my $new_addr = $fields[2];
1003		my $new_name = $fields[9];
1004
1005		if ($new_addr eq $last_addr) {
1006			push @dups, $new_name;
1007		} else {
1008			ProcSymSortOutMsg($RefTtl, $RelPath, $secname,
1009			    $last_addr, @dups) if (scalar(@dups) > 1);
1010			@dups = ( $new_name );
1011			$last_addr = $new_addr;
1012		}
1013	}
1014
1015	ProcSymSortOutMsg($RefTtl, $RelPath, $secname, $last_addr, @dups)
1016		if (scalar(@dups) > 1);
1017
1018	close SORT;
1019}
1020
1021
1022sub ProcDir {
1023	my($FullDir, $RelDir) = @_;
1024	my($NewFull, $NewRel);
1025
1026	# Determine if this is a directory we don't care about.
1027	if (!$opt{a}) {
1028		if ($RelDir =~ $SkipDirs) {
1029			return;
1030		}
1031	}
1032
1033	# Open the directory and read each entry, omit files starting with "."
1034	if (opendir(DIR, $FullDir)) {
1035		foreach my $Entry (readdir(DIR)) {
1036			if ($Entry =~ /^\./) {
1037				next;
1038			}
1039			$NewFull = "$FullDir/$Entry";
1040
1041			# Ignore symlinks.
1042			if (-l $NewFull) {
1043				next;
1044			}
1045			if (!stat($NewFull)) {
1046				next;
1047			}
1048			$NewRel = "$RelDir/$Entry";
1049
1050			# Descend into and process any directories.
1051			if (-d _) {
1052				ProcDir($NewFull, $NewRel);
1053				next;
1054			}
1055
1056			# Typically dynamic objects are executable, so we can
1057			# reduce the overall cost of this script (a lot!) by
1058			# screening out non-executables here, rather than pass
1059			# them to file(1) later.  However, it has been known
1060			# for shared objects to be mistakenly left non-
1061			# executable, so with -a let all files through so that
1062			# this requirement can be verified (see ProcFile()).
1063			if (!$opt{a}) {
1064				if (! -x _) {
1065					next;
1066				}
1067			}
1068
1069			# Process any standard files.
1070			if (-f _) {
1071				my($Secure) = 0;
1072
1073				if (-u _ || -g _) {
1074					$Secure = 1;
1075				}
1076
1077				ProcFile($NewFull, $NewRel, $Entry, $Secure);
1078				next;
1079			}
1080
1081		}
1082		closedir(DIR);
1083	}
1084}
1085
1086# Create a crle(1) script for any 64-bit dependencies we locate.  A runtime
1087# configuration file will be generated to establish alternative dependency
1088# mappings for all these dependencies.
1089
1090sub Entercrle64 {
1091	my($FullDir, $RelDir, $Entry) = @_;
1092
1093	if (!$Crle64) {
1094		# Create and initialize the script if is doesn't already exit.
1095
1096		$Crle64 = "$Tmpdir/$Prog.crle64.$$";
1097		open(CRLE64, "> $Crle64") ||
1098			die "$Prog: open failed: $Crle64: $!";
1099
1100		print CRLE64 "#!/bin/sh\ncrle -64\\\n";
1101	}
1102	print CRLE64 "\t-o $FullDir -a $RelDir/$Entry \\\n";
1103}
1104
1105# Create a crle(1) script for any 32-bit dependencies we locate.  A runtime
1106# configuration file will be generated to establish alternative dependency
1107# mappings for all these dependencies.
1108
1109sub Entercrle32 {
1110	my($FullDir, $RelDir, $Entry) = @_;
1111
1112	if (!$Crle32) {
1113		# Create and initialize the script if is doesn't already exit.
1114
1115		$Crle32 = "$Tmpdir/$Prog.crle32.$$";
1116		open(CRLE32, "> $Crle32") ||
1117			die "$Prog: open failed: $Crle32: $!";
1118
1119		print CRLE32 "#!/bin/sh\ncrle \\\n";
1120	}
1121	print CRLE32 "\t-o $FullDir -a $RelDir/$Entry \\\n";
1122}
1123
1124# Having finished gathering dependencies, complete any crle(1) scripts and
1125# execute them to generate the associated runtime configuration files.  In
1126# addition establish the environment variable required to pass the configuration
1127# files to ldd(1).
1128
1129sub GenConf {
1130	if ($Crle64) {
1131		$Conf64 = "$Tmpdir/$Prog.conf64.$$";
1132		print CRLE64 "\t-c $Conf64\n";
1133
1134		chmod 0755, $Crle64;
1135		close CRLE64;
1136
1137		if (system($Crle64)) {
1138			undef $Conf64;
1139		}
1140	}
1141	if ($Crle32) {
1142		$Conf32 = "$Tmpdir/$Prog.conf32.$$";
1143		print CRLE32 "\t-c $Conf32\n";
1144
1145		chmod 0755, $Crle32;
1146		close CRLE32;
1147
1148		if (system($Crle32)) {
1149			undef $Conf32;
1150		}
1151	}
1152
1153	if ($Crle64 && $Conf64 && $Crle32 && $Conf32) {
1154		$Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32";
1155	} elsif ($Crle64 && $Conf64) {
1156		$Env = "-e LD_FLAGS=config_64=$Conf64";
1157	} elsif ($Crle32 && $Conf32) {
1158		$Env = "-e LD_FLAGS=config_32=$Conf32";
1159	}
1160}
1161
1162# Recurse through a directory hierarchy looking for appropriate dependencies.
1163
1164sub GetDeps {
1165	my($FullDir, $RelDir) = @_;
1166	my($NewFull);
1167
1168	# Open the directory and read each entry, omit files starting with "."
1169	if (opendir(DIR, $FullDir)) {
1170		 foreach my $Entry (readdir(DIR)) {
1171			if ($Entry =~ /^\./) {
1172				next;
1173			}
1174			$NewFull = "$FullDir/$Entry";
1175
1176			# We need to follow links so that any dependencies
1177			# are expressed in all their available forms.
1178			# Bail on symlinks like 32 -> .
1179			if (-l $NewFull) {
1180				if (readlink($NewFull) =~ /^\.$/) {
1181					next;
1182				}
1183			}
1184			if (!stat($NewFull)) {
1185				next;
1186			}
1187
1188			if (!$opt{a}) {
1189				if ($NewFull =~ $SkipCrleConf) {
1190					next;
1191				}
1192			}
1193
1194			# If this is a directory descend into it.
1195			if (-d _) {
1196				my($NewRel);
1197
1198				if ($RelDir =~ /^\/$/) {
1199					$NewRel = "$RelDir$Entry";
1200				} else {
1201					$NewRel = "$RelDir/$Entry";
1202				}
1203
1204				GetDeps($NewFull, $NewRel);
1205				next;
1206			}
1207
1208			# If this is a regular file determine if its a
1209			# valid ELF dependency.
1210			if (-f _) {
1211				my($File);
1212
1213				# Typically shared object dependencies end with
1214				# ".so" or ".so.?", hence we can reduce the cost
1215				# of this script (a lot!) by screening out files
1216				# that don't follow this pattern.
1217				if (!$opt{a}) {
1218					if ($Entry !~ /\.so(?:\.\d+)*$/) {
1219						next;
1220					}
1221				}
1222
1223				$File = `file $NewFull`;
1224				if ($File !~ /dynamic lib/) {
1225					next;
1226				}
1227
1228				if ($File =~ /32-bit/) {
1229					Entercrle32($FullDir, $RelDir, $Entry);
1230				} elsif ($Ena64) {
1231					Entercrle64($FullDir, $RelDir, $Entry);
1232				}
1233				next;
1234			}
1235		}
1236		closedir(DIR);
1237	}
1238}
1239exit $Error
1240