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