xref: /illumos-gate/usr/src/tools/scripts/check_rtime.pl (revision a07094369b21309434206d9b3601d162693466fc)
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 2006 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);
63use vars  qw($SkipUndefDirs $SkipUndefFiles $SkipUnusedDirs $SkipUnusedFiles);
64use vars  qw($SkipStabFiles $SkipNoExStkFiles);
65use vars  qw($UnusedNoise $Prog $Mach $Isalist $Env $Ena64 $Tmpdir $Error);
66use vars  qw($UnusedFiles $UnusedPaths $LddNoU $Crle32 $Crle64 $Conf32 $Conf64);
67use vars  qw($SkipInterps $OldDeps %opt);
68
69use strict;
70
71
72# Define any directories we should skip completely.
73$SkipDirs = qr{
74	etc/lib |			# special - used for partial statics
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	ld\.so\.1 |			# confusing but correct dependencies
85	lddstub |			# lddstub has no dependencies
86	libmakestate\.so\.1 |		# temporary; delivered by compiler group
87	libm\.so\.1 |			# temporary; delivered by compiler group
88	libm\.so\.2 |			# temporary; delivered by compiler group
89	geniconvtbl\.so |		# 4384329
90	libssagent\.so\.1 |		# 4328854
91	libdmi\.so\.1 |			#  "  "
92	libdmici\.so\.1 |		#  "  "
93	libdmimi\.so\.1 |		#  "  "
94	libpsvcplugin_psr\.so\.1 |	# 4385799
95	libpsvcpolicy_psr\.so\.1 |	#  "  "
96	libpsvcpolicy\.so\.1 |		#  "  "
97	picl_slm\.so |			#  "  "
98	libcrypto_extra\.so\.0\.9\.7 |	# OpenSSL SUNWcry filter lib
99	libssl_extra\.so\.0\.9\.7 |	# OpenSSL SUNWcry filter lib
100	fcpackage\.so |			# circular dependency on fcthread.so
101	grub
102	)$
103}x;
104
105# Define any files that are allowed text relocations.
106$SkipTextrelFiles = qr{ ^(?:
107	unix |				# kernel models are non-pic
108	mdb				# relocations against __RTC (dbx)
109	)$
110}x;
111
112# Define any files that are allowed undefined references.
113$SkipUndefDirs = qr{
114	usr/lib/inet/ppp/ |		# pppd plugins have callbacks
115	usr/lib/libp/ |			# libc.so.1 requires _mcount
116	usr/lib/vold/ |			# vold dependencies have callbacks
117	usr/lib/rmmount |		# rmmount actions have callbacks
118	/lib/mdb/ |			# mdb modules have callbacks
119	/lib/fm/fmd/plugins/ |		# fmd modules have callbacks
120	/lib/fm/fmd/schemes/ |		# fmd schemes have callbacks
121	/i86pc/lib/mtst/		# mtst modules have callbacks
122}x;
123
124$SkipUndefFiles = qr{ ^(?:
125	libthread_db\.so\.0 |		# callbacks to proc service interface
126	libthread_db\.so\.1 |		#  "	"	"	"
127	librtld_db\.so\.1 |		#  "	"	"	"
128	libc_db\.so\.1 |		#  "	"	"	"
129	libldstab\.so\.1 |		# link-edit support libraries have
130	libld\.so\.3 |			# callback to the link-editors
131	libld\.so\.2 |			#  "	"	"	"
132	liblddbg\.so\.4 |		#  "	"	"	"
133	librtld\.so\.1 |		#  "	"	"	"
134	libnisdb\.so\.2 |		# C++
135	libsvm\.so\.1 |			# libspmicommon.so.1 lacking
136	libwanboot\.so\.1 |		# libcrypto.a and libssl.a
137	libwrap\.so\.1\.0 |		# uses symbols provided by application
138	fcthread\.so |			# uses symbols provided by application
139	fn\.so\.2 |			# callback to automount
140	preen_md\.so\.1 |		# callback to driver
141	libike\.so\.1 |			# callbacks to in.iked for IKE policy
142	devfsadmd_mod\.so |		# sysevent module callback to syseventd
143	sysevent_conf_mod\.so |		# sysevent module callback to syseventd
144	sysevent_reg_mod\.so		# sysevent module callback to syseventd
145	)$
146}x;
147
148# Define any files that have unused dependencies.
149$SkipUnusedDirs = qr{
150	lib/picl/plugins/ |		# require devtree dependencies
151	/lib/libp			# profile libc makes libm an unused
152}x;					#	dependency of standard libc
153
154$SkipUnusedFiles = qr{ ^(?:
155	devfsadm |			# 4382889
156	disks |				#  "  "
157	tapes |				#  "  "
158	ports |				#  "  "
159	audlinks |			#  "  "
160	devlinks |			#  "  "
161	drvconfig |			#  "  "
162	ntptrace |			# on intel doesn't need libmd5
163	rmmount |			# 4418770, volmgt dependency is required
164					#	to compensate for SunPCi.
165	ocfserv |			# libsched unreference by libjvm,
166	poold |				#	see 4952319.
167	libc\.so\.1\.9 |		# 4lib/libc versions have private
168	libc\.so\.2\.9			#	copies of stuff from libc.
169	)$
170}x;
171
172# Define any files that should contain debugging information.
173$SkipStabFiles = qr{ ^(?:
174	abi_.* |
175	interceptors\.so\.1 |
176	unix
177	)$
178}x;
179
180# Define any files that don't require a non-executable stack definition.
181$SkipNoExStkFiles = qr{ ^(?:
182	forth |
183	unix |
184	multiboot
185	)$
186}x;
187
188# Define any files that should only have unused (ldd -u) processing.
189$UnusedPaths = qr{
190	ucb/shutdown			# libucb interposes on libc and makes
191					# dependencies on libc seem unnecessary
192}x;
193
194$UnusedFiles = qr{ ^(?:
195	rpc\.nisd			# CCNEEDED makes pthread unreferenced
196	)$
197}x;
198
199# Define unused dependencies we should ignore.
200# libCrun has a unnecessary dependency on libw, and libmapmalloc is often
201# defined to interpose on libc but isn't used by the application itself.
202# Threads dependencies look unused if libc is bound first.
203$UnusedNoise = qr{
204	libw\.so\.1;\ unused |
205	unused\ object=.*libw\.so\.1 |
206	libthread\.so\.1;\ unused |
207	libpthread\.so\.1;\ unused |
208	unused\ object=.*libpthread\.so\.1 |
209	libnsl\.so\.1;\ unused\ dependency\ of\ .*libxslt\.so\.1 |
210	libdl\.so\.1;\ unused\ dependency\ of\ .*libspmicommon\.so\.1 |
211	libdl\.so\.1;\ unused\ dependency\ of\ .*libCrun\.so\.1 |
212	libfru\.so\.1;\ unused\ object=.*libdl\.so\.1 |
213	libfrupicl\.so\.1;\ unused\ object=.*libdl\.so\.1 |
214	libmapmalloc\.so\.1;\ unused |
215	unused\ dependency\ of\ .*libstdc\+\+\.so\.6 |
216	unreferenced\ object=.*libstdc\+\+\.so\.6 |
217	unused\ dependency\ of\ .*libnetsnmphelpers\.so\.5 |
218	unused\ dependency\ of\ .*libnetsnmpmibs\.so\.5 |
219	unused\ dependency\ of\ .*libnetsnmpagent\.so\.5
220}x;
221
222# Define interpreters we should ignore.
223$SkipInterps = qr{
224	misc/krtld |
225	misc/amd64/krtld |
226	misc/sparcv9/krtld
227}x;
228
229# Catch libintl and libw, although ld(1) will bind to these and thus determine
230# they're needed, their content was moved into libc as of on297 build 7.
231# libthread and libpthread were completely moved into libc as of on10 build 53.
232# Also, catch libdl, whose content was moved into libc as of on10 build 49.
233$OldDeps = qr{ ^(?:
234	libintl\.so\.1 |
235	libw\.so\.1 |
236	libthread\.so\.1 |
237	libpthread\.so\.1 |
238	libdl\.so\.1
239	)$
240}x;
241
242use Getopt::Std;
243
244# -----------------------------------------------------------------------------
245
246# Reliably compare two OS revisions.  Arguments are <ver1> <op> <ver2>.
247# <op> is the string form of a normal numeric comparison operator.
248sub cmp_os_ver {
249	my @ver1 = split(/\./, $_[0]);
250	my $op = $_[1];
251	my @ver2 = split(/\./, $_[2]);
252
253	push @ver2, ("0") x $#ver1 - $#ver2;
254	push @ver1, ("0") x $#ver2 - $#ver1;
255
256	my $diff = 0;
257	while (@ver1 || @ver2) {
258		if (($diff = shift(@ver1) - shift(@ver2)) != 0) {
259			last;
260		}
261	}
262	return (eval "$diff $op 0" ? 1 : 0);
263}
264
265# Establish a program name for any error diagnostics.
266chomp($Prog = `basename $0`);
267
268# Determine what machinery is available.
269$Mach = `uname -p`;
270$Isalist = `isalist`;
271$Env = "";
272if ($Mach =~ /sparc/) {
273	if ($Isalist =~ /sparcv9/) {
274		$Ena64 = "ok";
275	}
276} elsif ($Mach =~ /i386/) {
277	if ($Isalist =~ /amd64/) {
278		$Ena64 = "ok";
279	}
280}
281
282# Check that we have arguments.
283if ((getopts('ad:imos', \%opt) == 0) || ($#ARGV == -1)) {
284	print "usage: $Prog [-a] [-d depdir] [-m] [-o] [-s] file | dir, ...\n";
285	print "\t[-a]\t\tprocess all files (ignore any exception lists)\n";
286	print "\t[-d dir]\testablish dependencies from under directory\n";
287	print "\t[-i]\t\tproduce dynamic table entry information\n";
288	print "\t[-m]\t\tprocess mcs(1) comments\n";
289	print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
290	print "\t[-s]\t\tprocess .stab and .symtab entries\n";
291	exit 1;
292} else {
293	my($Proto);
294
295	if ($opt{d}) {
296		# User specified dependency directory - make sure it exists.
297		if (! -d $opt{d}) {
298			print "$Prog: $opt{d} is not a directory\n";
299			exit 1;
300		}
301		$Proto = $opt{d};
302
303	} elsif ($ENV{CODEMGR_WS}) {
304		my($Root);
305
306		# Without a user specified dependency directory see if we're
307		# part of a codemanager workspace and if a proto area exists.
308		if (($Root = $ENV{ROOT}) && (-d $Root)) {
309			$Proto = $Root;
310		}
311	}
312
313	if (!($Tmpdir = $ENV{TMPDIR}) || (! -d $Tmpdir)) {
314		$Tmpdir = "/tmp";
315	}
316
317	# Look for dependencies under $Proto.
318	if ($Proto) {
319		# To support alternative dependency mapping we'll need ldd(1)'s
320		# -e option.  This is relatively new (s81_30), so make sure
321		# ldd(1)is capable before gathering any dependency information.
322		if (system('ldd -e /usr/lib/lddstub 2> /dev/null')) {
323			print "ldd: does not support -e, unable to ";
324			print "create alternative dependency mappingings.\n";
325			print "ldd: option added under 4390308 (s81_30).\n\n";
326		} else {
327			# Gather dependencies and construct a alternative
328			# dependency mapping via a crle(1) configuration file.
329			GetDeps($Proto, "/");
330			GenConf();
331		}
332	}
333
334	# To support unreferenced dependency detection we'll need ldd(1)'s -U
335	# option.  This is relatively new (4638070), and if not available we
336	# can still fall back to -u.  Even with this option, don't use -U with
337	# releases prior to 5.10 as the cleanup for -U use only got integrated
338	# into 5.10 under 4642023.  Note, that nightly doesn't typically set a
339	# RELEASE from the standard <env> files.  Users who wish to disable use
340	# of ldd(1)'s -U should set (or uncomment) RELEASE in their <env> file
341	# if using nightly, or otherwise establish it in their environment.
342	if (system('ldd -U /usr/lib/lddstub 2> /dev/null')) {
343		$LddNoU = 1;
344	} else {
345		my($Release);
346
347		if (($Release = $ENV{RELEASE}) &&
348		    (cmp_os_ver($Release, "<", "5.10"))) {
349			$LddNoU = 1;
350		} else {
351			$LddNoU = 0;
352		}
353	}
354
355	# For each argument determine if we're dealing with a file or directory.
356	foreach my $Arg (@ARGV) {
357		# Ignore symbolic links.
358		if (-l $Arg) {
359			next;
360		}
361
362		if (!stat($Arg)) {
363			next;
364		}
365
366		# Process simple files.
367		if (-f _) {
368			my($RelPath) = $Arg;
369			my($File) = $Arg;
370			my($Secure) = 0;
371
372			$RelPath =~ s!^.*/!./!;
373			$File =~ s!^.*/!!;
374
375			if (-u _ || -g _) {
376				$Secure = 1;
377			}
378
379			ProcFile($Arg, $RelPath, $File, $Secure);
380			next;
381		}
382		# Process directories.
383		if (-d _) {
384			ProcDir($Arg, ".");
385			next;
386		}
387
388		print "$Arg is not a file or directory\n";
389		$Error = 1;
390	}
391
392	# Cleanup
393	CleanUp();
394}
395
396$Error = 0;
397
398# Clean up and temporary files.
399sub CleanUp {
400	if ($Crle64) {
401		unlink $Crle64;
402	}
403	if ($Conf64) {
404		unlink $Conf64;
405	}
406	if ($Crle32) {
407		unlink $Crle32;
408	}
409	if ($Conf32) {
410		unlink $Conf32;
411	}
412}
413
414# Create an output message, either a one-liner (under -o) or preceded by the
415# files relative pathname as a title.
416sub OutMsg {
417	my($Ttl, $Path, $Msg) = @_;
418
419	if ($opt{o}) {
420		$Msg =~ s/^[ \t]*//;
421		print "$Path: $Msg\n";
422	} else {
423		if ($Ttl eq 0) {
424			print "==== $Path ====\n";
425		}
426		print "$Msg\n";
427	}
428}
429
430# Determine whether this a ELF dynamic object and if so investigate its runtime
431# attributes.
432sub ProcFile {
433	my($FullPath, $RelPath, $File, $Secure) = @_;
434	my(@Elf, @Ldd, $Dyn, $Intp, $Dll, $Ttl, $Sym, $Interp, $Stack);
435	my($Sun, $Relsz, $Pltsz, $Uns, $Tex, $Stab, $Strip, $Lddopt);
436	my($Val, $Header, $SkipLdd, $IsX86, $RWX);
437
438	# Ignore symbolic links.
439	if (-l $FullPath) {
440		return;
441	}
442
443	$Ttl = 0;
444	@Ldd = 0;
445
446	# Determine whether we have access to inspect the file.
447	if (!(-r $FullPath)) {
448		OutMsg($Ttl++, $RelPath,
449		    "\tunable to inspect file: permission denied");
450		return;
451	}
452
453	# Determine if this is a file we don't care about.
454	if (!$opt{a}) {
455		if ($File =~ $SkipFiles) {
456			return;
457		}
458	}
459
460	# Determine whether we have a executable (static or dynamic) or a
461	# shared object.
462	@Elf = split(/\n/, `elfdump -epdic $FullPath 2>&1`);
463
464	$Dyn = $Intp = $Dll = $Stack = $IsX86 = $RWX = 0;
465	$Interp = 1;
466	$Header = 'None';
467	foreach my $Line (@Elf) {
468		# If we have an invalid file type (which we can tell from the
469		# first line), or we're processing an archive, bail.
470		if ($Header eq 'None') {
471			if (($Line =~ /invalid file/) ||
472			    ($Line =~ /$FullPath(.*):/)) {
473				return;
474			}
475		}
476
477		if ($Line =~ /^ELF Header/) {
478			$Header = 'Ehdr';
479
480		} elsif ($Line =~ /^Program Header/) {
481			$Header = 'Phdr';
482			$RWX = 0;
483
484		} elsif ($Line =~ /^Interpreter/) {
485			$Header = 'Intp';
486
487		} elsif ($Line =~ /^Dynamic Section/) {
488			# A dynamic section indicates we're a dynamic object
489			# (this makes sure we don't check static executables).
490			$Dyn = 1;
491
492		} elsif (($Header eq 'Ehdr') && ($Line =~ /e_type:/)) {
493			# The e_type field indicates whether this file is a
494			# shared object (ET_DYN) or an executable (ET_EXEC).
495			if ($Line =~ /ET_DYN/) {
496				$Dll = 1;
497			} elsif ($Line !~ /ET_EXEC/) {
498				return;
499			}
500		} elsif (($Header eq 'Ehdr') && ($Line =~ /ei_class:/)) {
501			# If we encounter a 64-bit object, but we're not running
502			# on a 64-bit system, suppress calling ldd(1).
503			if (($Line =~ /ELFCLASS64/) && !$Ena64) {
504				$SkipLdd = 1;
505			}
506		} elsif (($Header eq 'Ehdr') && ($Line =~ /e_machine:/)) {
507			# If it's a X86 object, we need to enforce RW- data.
508			if (($Line =~ /(EM_AMD64|EM_386)/)) {
509				$IsX86 = 1;
510			}
511		} elsif (($Header eq 'Phdr') &&
512		    ($Line =~ /\[ PF_X  PF_W  PF_R \]/)) {
513			# RWX segment seen.
514			$RWX = 1;
515
516		} elsif (($Header eq 'Phdr') &&
517		    ($Line =~ /\[ PT_LOAD \]/ && $RWX && $IsX86)) {
518			# Seen an RWX PT_LOAD segment.
519			if ($File !~ $SkipNoExStkFiles) {
520				OutMsg($Ttl++, $RelPath,
521				    "\tapplication requires non-executable " .
522				    "data\t<no -Mmapfile_noexdata?>");
523			}
524
525		} elsif (($Header eq 'Phdr') &&
526		    ($Line =~ /\[ PT_SUNWSTACK \]/)) {
527			# This object defines a non-executable stack.
528			$Stack = 1;
529
530		} elsif (($Header eq 'Intp') && !$opt{a} &&
531		    ($Line =~ $SkipInterps)) {
532			# This object defines an interpretor we should skip.
533			$Interp = 0;
534		}
535	}
536
537	# Determine whether this ELF executable or shared object has a
538	# conforming mcs(1) comment section.  If the correct $(POST_PROCESS)
539	# macros are used, only a 3 or 4 line .comment section should exist
540	# containing one or two "@(#)SunOS" identifying comments (one comment
541	# for a non-debug build, and two for a debug build). The results of
542	# the following split should be three or four lines, the last empty
543	# line being discarded by the split.
544	if ($opt{m}) {
545		my(@Mcs, $Con, $Dev);
546
547		@Mcs = split(/\n/, `mcs -p $FullPath 2>&1`);
548
549		$Con = $Dev = $Val = 0;
550		foreach my $Line (@Mcs) {
551			$Val++;
552
553			if (($Val == 3) && ($Line !~ /^@\(#\)SunOS/)) {
554				$Con = 1;
555				last;
556			}
557			if (($Val == 4) && ($Line =~ /^@\(#\)SunOS/)) {
558				$Dev = 1;
559				next;
560			}
561			if (($Dev == 0) && ($Val == 4)) {
562				$Con = 1;
563				last;
564			}
565			if (($Dev == 1) && ($Val == 5)) {
566				$Con = 1;
567				last;
568			}
569		}
570		if ($opt{m} && ($Con == 1)) {
571			OutMsg($Ttl++, $RelPath,
572			    "\tnon-conforming mcs(1) comment\t<no \$(POST_PROCESS)?>");
573		}
574	}
575
576	# Applications should contain a non-executable stack definition.
577	if (($Dll == 0) && ($Stack == 0)) {
578		if (!$opt{a}) {
579			if ($File =~ $SkipNoExStkFiles) {
580				goto DYN;
581			}
582		}
583		OutMsg($Ttl++, $RelPath,
584		    "\tapplication requires non-executable stack\t<no -Mmapfile_noexstk?>");
585	}
586
587DYN:
588	# Having caught any static executables in the mcs(1) check and non-
589	# executable stack definition check, continue with dynamic objects
590	# from now on.
591	if ($Dyn eq 0) {
592		return;
593	}
594
595	# Only use ldd unless we've encountered an interpreter that should
596	# ne skipped.
597	if (!$SkipLdd && $Interp) {
598		if ($Secure) {
599			# The execution of a secure application over an nfs file
600			# system mounted nosuid will result in warning messages
601			# being sent to /var/adm/messages.  As this type of
602			# environment can occur with root builds, move the file
603			# being investigated to a safe place first.  In addition
604			# remove its secure permission so that it can be
605			# influenced by any alternative dependency mappings.
606
607			my($TmpPath) = "$Tmpdir/$File";
608
609			system('cp', $FullPath, $TmpPath);
610			chmod 0777, $TmpPath;
611			$FullPath = $TmpPath;
612		}
613
614		# Use ldd(1) to determine the objects relocatability and use.
615		# By default look for all unreferenced dependencies.  However,
616		# some objects have legitimate dependencies that they do not
617		# reference.
618		if ($LddNoU || ($File =~ $UnusedFiles) ||
619		    ($RelPath =~ $UnusedPaths)) {
620			$Lddopt = "-ru";
621		} else {
622			$Lddopt = "-rU";
623		}
624		@Ldd = split(/\n/, `ldd $Lddopt $Env $FullPath 2>&1`);
625		if ($Secure) {
626			unlink $FullPath;
627		}
628	}
629
630	$Val = 0;
631	$Sym = 5;
632	$Uns = 1;
633
634LDD:	foreach my $Line (@Ldd) {
635
636		if ($Val == 0) {
637			$Val = 1;
638			# Make sure ldd(1) worked.  One possible failure is that
639			# this is an old ldd(1) prior to -e addition (4390308).
640			if ($Line =~ /usage:/) {
641				$Line =~ s/$/\t<old ldd(1)?>/;
642				OutMsg($Ttl++, $RelPath, $Line);
643				last;
644			} elsif ($Line =~ /execution failed/) {
645				OutMsg($Ttl++, $RelPath, $Line);
646				last;
647			}
648
649			# It's possible this binary can't be executed, ie. we've
650			# found a sparc binary while running on an intel system,
651			# or a sparcv9 binary on a sparcv7/8 system.
652			if ($Line =~ /wrong class/) {
653				OutMsg($Ttl++, $RelPath,
654				    "\thas wrong class or data encoding");
655				next;
656			}
657
658			# Historically, ldd(1) likes executable objects to have
659			# their execute bit set.  Note that this test isn't
660			# applied unless the -a option is in effect, as any
661			# non-executable files are skipped by default to reduce
662			# the cost of running this script.
663			if ($Line =~ /not executable/) {
664				OutMsg($Ttl++, $RelPath,
665				    "\tis not executable");
666				next;
667			}
668		}
669
670		# Look for "file" or "versions" that aren't found.  Note that
671		# these lines will occur before we find any symbol referencing
672		# errors.
673		if (($Sym == 5) && ($Line =~ /not found\)/)) {
674			if ($Line =~ /file not found\)/) {
675				$Line =~ s/$/\t<no -zdefs?>/;
676			}
677			OutMsg($Ttl++, $RelPath, $Line);
678			next;
679		}
680		# Look for relocations whose symbols can't be found.  Note, we
681		# only print out the first 5 relocations for any file as this
682		# output can be excessive.
683		if ($Sym && ($Line =~ /symbol not found/)) {
684			# Determine if this file is allowed undefined
685			# references.
686			if ($Sym == 5) {
687				if (!$opt{a}) {
688					if ($RelPath =~ $SkipUndefDirs) {
689						$Sym = 0;
690						next LDD;
691					}
692					if ($File =~ $SkipUndefFiles) {
693						$Sym = 0;
694						next LDD;
695					}
696				}
697			}
698			if ($Sym-- == 1) {
699				if (!$opt{o}) {
700					OutMsg($Ttl++, $RelPath,
701					    "\tcontinued ...");
702				}
703				next;
704			}
705			# Just print the symbol name.
706			$Line =~ s/$/\t<no -zdefs?>/;
707			OutMsg($Ttl++, $RelPath, $Line);
708			next;
709		}
710		# Look for any unused dependencies.
711		if ($Uns && ($Line =~ /unused/)) {
712			if (!$opt{a}) {
713				if ($RelPath =~ $SkipUnusedDirs) {
714					$Uns = 0;
715					next LDD;
716				}
717				if ($File =~ $SkipUnusedFiles) {
718					$Uns = 0;
719					next LDD;
720				}
721
722				# Remove any noise.
723				if ($Line =~ $UnusedNoise) {
724					$Uns = 0;
725					next LDD;
726				}
727			}
728			if ($Secure) {
729				$Line =~ s!$Tmpdir/!!;
730			}
731			$Line =~ s/^[ \t]*(.*)/\t$1\t<remove lib or -zignore?>/;
732			OutMsg($Ttl++, $RelPath, $Line);
733			next;
734		}
735	}
736
737	# Reuse the elfdump(1) data to investigate additional dynamic linking
738	# information.
739
740	$Sun = $Relsz = $Pltsz = $Dyn = $Stab = 0;
741	$Tex = $Strip = 1;
742
743	$Header = 'None';
744ELF:	foreach my $Line (@Elf) {
745		# We're only interested in the section headers and the dynamic
746		# section.
747		if ($Line =~ /^Section Header/) {
748			$Header = 'Shdr';
749
750			if (($Sun == 0) && ($Line =~ /\.SUNW_reloc/)) {
751				# This object has a combined relocation section.
752				$Sun = 1;
753
754			} elsif (($Stab == 0) && ($Line =~ /\.stab/)) {
755				# This object contain .stabs sections
756				$Stab = 1;
757			}
758
759			if (($Strip == 1) && ($Line =~ /\.symtab/)) {
760				# This object contains a complete symbol table.
761				$Strip = 0;
762			}
763			next;
764
765		} elsif ($Line =~ /^Dynamic Section/) {
766			$Header = 'Dyn';
767			next;
768		} elsif ($Header ne 'Dyn') {
769			next;
770		}
771
772		# Does this object contain text relocations.
773		if ($Tex && ($Line =~ /TEXTREL/)) {
774			# Determine if this file is allowed text relocations.
775			if (!$opt{a}) {
776				if ($File =~ $SkipTextrelFiles) {
777					$Tex = 0;
778					next ELF;
779				}
780			}
781			OutMsg($Ttl++, $RelPath,
782			    "\tTEXTREL .dynamic tag\t\t\t<no -Kpic?>");
783			$Tex = 0;
784			next;
785		}
786
787		# Does this file have any relocation sections (there are a few
788		# psr libraries with no relocations at all, thus a .SUNW_reloc
789		# section won't exist either).
790		if (($Relsz == 0) && ($Line =~ / RELA?SZ/)) {
791			$Relsz = hex((split(' ', $Line))[2]);
792			next;
793		}
794
795		# Does this file have any plt relocations.  If the plt size is
796		# equivalent to the total relocation size then we don't have
797		# any relocations suitable for combining into a .SUNW_reloc
798		# section.
799		if (($Pltsz == 0) && ($Line =~ / PLTRELSZ/)) {
800			$Pltsz = hex((split(' ', $Line))[2]);
801			next;
802		}
803
804		# Under the -i (information) option print out any useful dynamic
805		# entries.
806		# Does this object have any dependencies.
807		if ($opt{i} && ($Line =~ /NEEDED/)) {
808			my($Need) = (split(' ', $Line))[3];
809
810			# Catch any old (unnecessary) dependencies.
811			if ($Need =~ $OldDeps) {
812				OutMsg($Ttl++, $RelPath,
813				    "\tNEEDED=$Need\t<dependency no longer necessary>");
814			} else {
815				OutMsg($Ttl++, $RelPath, "\tNEEDED=$Need");
816			}
817			next;
818		}
819
820		# Does this object specify a runpath.
821		if ($opt{i} && ($Line =~ /RPATH/)) {
822			my($Rpath) = (split(' ', $Line))[3];
823			OutMsg($Ttl++, $RelPath, "\tRPATH=$Rpath");
824			next;
825		}
826	}
827
828	# A shared object, that contains non-plt relocations, should have a
829	# combined relocation section indicating it was built with -z combreloc.
830	if ($Dll && $Relsz && ($Relsz != $Pltsz) && ($Sun == 0)) {
831		OutMsg($Ttl++, $RelPath,
832		    "\tSUNW_reloc section missing\t\t<no -zcombreloc?>");
833	}
834
835	# No objects released to a customer should have any .stabs sections
836	# remaining, they should be stripped.
837	if ($opt{s} && $Stab) {
838		if (!$opt{a}) {
839			if ($File =~ $SkipStabFiles) {
840				goto DONESTAB;
841			}
842		}
843		OutMsg($Ttl++, $RelPath,
844		    "\tdebugging sections should be deleted\t<no strip -x?>");
845	}
846
847DONESTAB:
848
849	# All objects should have a full symbol table to provide complete
850	# debugging stack traces.
851	if ($Strip) {
852		OutMsg($Ttl++, $RelPath,
853		    "\tsymbol table should not be stripped\t<remove -s?>");
854	}
855}
856
857
858sub ProcDir {
859	my($FullDir, $RelDir) = @_;
860	my($NewFull, $NewRel);
861
862	# Determine if this is a directory we don't care about.
863	if (!$opt{a}) {
864		if ($RelDir =~ $SkipDirs) {
865			return;
866		}
867	}
868
869	# Open the directory and read each entry, omit files starting with "."
870	if (opendir(DIR, $FullDir)) {
871		foreach my $Entry (readdir(DIR)) {
872			if ($Entry =~ /^\./) {
873				next;
874			}
875			$NewFull = "$FullDir/$Entry";
876
877			# Ignore symlinks.
878			if (-l $NewFull) {
879				next;
880			}
881			if (!stat($NewFull)) {
882				next;
883			}
884			$NewRel = "$RelDir/$Entry";
885
886			# Descend into and process any directories.
887			if (-d _) {
888				ProcDir($NewFull, $NewRel);
889				next;
890			}
891
892			# Typically dynamic objects are executable, so we can
893			# reduce the overall cost of this script (a lot!) by
894			# screening out non-executables here, rather than pass
895			# them to file(1) later.  However, it has been known
896			# for shared objects to be mistakenly left non-
897			# executable, so with -a let all files through so that
898			# this requirement can be verified (see ProcFile()).
899			if (!$opt{a}) {
900				if (! -x _) {
901					next;
902				}
903			}
904
905			# Process any standard files.
906			if (-f _) {
907				my($Secure) = 0;
908
909				if (-u _ || -g _) {
910					$Secure = 1;
911				}
912
913				ProcFile($NewFull, $NewRel, $Entry, $Secure);
914				next;
915			}
916
917		}
918		closedir(DIR);
919	}
920}
921
922# Create a crle(1) script for any 64-bit dependencies we locate.  A runtime
923# configuration file will be generated to establish alternative dependency
924# mappings for all these dependencies.
925
926sub Entercrle64 {
927	my($FullDir, $RelDir, $Entry) = @_;
928
929	if (!$Crle64) {
930		# Create and initialize the script if is doesn't already exit.
931
932		$Crle64 = "$Tmpdir/$Prog.crle64.$$";
933		open(CRLE64, "> $Crle64") ||
934			die "$Prog: open failed: $Crle64: $!";
935
936		print CRLE64 "#!/bin/sh\ncrle -64\\\n";
937	}
938	print CRLE64 "\t-o $FullDir -a $RelDir/$Entry \\\n";
939}
940
941# Create a crle(1) script for any 32-bit dependencies we locate.  A runtime
942# configuration file will be generated to establish alternative dependency
943# mappings for all these dependencies.
944
945sub Entercrle32 {
946	my($FullDir, $RelDir, $Entry) = @_;
947
948	if (!$Crle32) {
949		# Create and initialize the script if is doesn't already exit.
950
951		$Crle32 = "$Tmpdir/$Prog.crle32.$$";
952		open(CRLE32, "> $Crle32") ||
953			die "$Prog: open failed: $Crle32: $!";
954
955		print CRLE32 "#!/bin/sh\ncrle \\\n";
956	}
957	print CRLE32 "\t-o $FullDir -a $RelDir/$Entry \\\n";
958}
959
960# Having finished gathering dependencies, complete any crle(1) scripts and
961# execute them to generate the associated runtime configuration files.  In
962# addition establish the environment variable required to pass the configuration
963# files to ldd(1).
964
965sub GenConf {
966	if ($Crle64) {
967		$Conf64 = "$Tmpdir/$Prog.conf64.$$";
968		print CRLE64 "\t-c $Conf64\n";
969
970		chmod 0755, $Crle64;
971		close CRLE64;
972
973		if (system($Crle64)) {
974			undef $Conf64;
975		}
976	}
977	if ($Crle32) {
978		$Conf32 = "$Tmpdir/$Prog.conf32.$$";
979		print CRLE32 "\t-c $Conf32\n";
980
981		chmod 0755, $Crle32;
982		close CRLE32;
983
984		if (system($Crle32)) {
985			undef $Conf32;
986		}
987	}
988
989	if ($Crle64 && $Conf64 && $Crle32 && $Conf32) {
990		$Env = "-e LD_FLAGS=config_64=$Conf64,config_32=$Conf32";
991	} elsif ($Crle64 && $Conf64) {
992		$Env = "-e LD_FLAGS=config_64=$Conf64";
993	} elsif ($Crle32 && $Conf32) {
994		$Env = "-e LD_FLAGS=config_32=$Conf32";
995	}
996}
997
998# Recurse through a directory hierarchy looking for appropriate dependencies.
999
1000sub GetDeps {
1001	my($FullDir, $RelDir) = @_;
1002	my($NewFull);
1003
1004	# Open the directory and read each entry, omit files starting with "."
1005	if (opendir(DIR, $FullDir)) {
1006		 foreach my $Entry (readdir(DIR)) {
1007			if ($Entry =~ /^\./) {
1008				next;
1009			}
1010			$NewFull = "$FullDir/$Entry";
1011
1012			# We need to follow links so that any dependencies
1013			# are expressed in all their available forms.
1014			# Bail on symlinks like 32 -> .
1015			if (-l $NewFull) {
1016				if (readlink($NewFull) =~ /^\.$/) {
1017					next;
1018				}
1019			}
1020			if (!stat($NewFull)) {
1021				next;
1022			}
1023
1024			# If this is a directory descend into it.
1025			if (-d _) {
1026				my($NewRel);
1027
1028				if ($RelDir =~ /^\/$/) {
1029					$NewRel = "$RelDir$Entry";
1030				} else {
1031					$NewRel = "$RelDir/$Entry";
1032				}
1033
1034				GetDeps($NewFull, $NewRel);
1035				next;
1036			}
1037
1038			# If this is a regular file determine if its a
1039			# valid ELF dependency.
1040			if (-f _) {
1041				my($File);
1042
1043				# Typically shared object dependencies end with
1044				# ".so" or ".so.?", hence we can reduce the cost
1045				# of this script (a lot!) by screening out files
1046				# that don't follow this pattern.
1047				if (!$opt{a}) {
1048					if ($Entry !~ /\.so(?:\.\d+)*$/) {
1049						next;
1050					}
1051				}
1052
1053				$File = `file $NewFull`;
1054				if ($File !~ /dynamic lib/) {
1055					next;
1056				}
1057
1058				if ($File =~ /32-bit/) {
1059					Entercrle32($FullDir, $RelDir, $Entry);
1060				} elsif ($Ena64) {
1061					Entercrle64($FullDir, $RelDir, $Entry);
1062				}
1063				next;
1064			}
1065		}
1066		closedir(DIR);
1067	}
1068}
1069exit $Error
1070