xref: /titanic_53/usr/src/tools/scripts/find_elf.pl (revision 75ce41a57ff334bd8fe2cb9ed51eea835892f944)
1*75ce41a5SAli Bahrami#!/usr/bin/perl -w
2*75ce41a5SAli Bahrami#
3*75ce41a5SAli Bahrami# CDDL HEADER START
4*75ce41a5SAli Bahrami#
5*75ce41a5SAli Bahrami# The contents of this file are subject to the terms of the
6*75ce41a5SAli Bahrami# Common Development and Distribution License (the "License").
7*75ce41a5SAli Bahrami# You may not use this file except in compliance with the License.
8*75ce41a5SAli Bahrami#
9*75ce41a5SAli Bahrami# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*75ce41a5SAli Bahrami# or http://www.opensolaris.org/os/licensing.
11*75ce41a5SAli Bahrami# See the License for the specific language governing permissions
12*75ce41a5SAli Bahrami# and limitations under the License.
13*75ce41a5SAli Bahrami#
14*75ce41a5SAli Bahrami# When distributing Covered Code, include this CDDL HEADER in each
15*75ce41a5SAli Bahrami# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*75ce41a5SAli Bahrami# If applicable, add the following below this CDDL HEADER, with the
17*75ce41a5SAli Bahrami# fields enclosed by brackets "[]" replaced with your own identifying
18*75ce41a5SAli Bahrami# information: Portions Copyright [yyyy] [name of copyright owner]
19*75ce41a5SAli Bahrami#
20*75ce41a5SAli Bahrami# CDDL HEADER END
21*75ce41a5SAli Bahrami#
22*75ce41a5SAli Bahrami
23*75ce41a5SAli Bahrami#
24*75ce41a5SAli Bahrami# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25*75ce41a5SAli Bahrami# Use is subject to license terms.
26*75ce41a5SAli Bahrami#
27*75ce41a5SAli Bahrami
28*75ce41a5SAli Bahrami#
29*75ce41a5SAli Bahrami# Find ELF executables and sharable objects
30*75ce41a5SAli Bahrami#
31*75ce41a5SAli Bahrami# This script descends a directory hierarchy and reports the ELF
32*75ce41a5SAli Bahrami# objects found, one object per line of output.
33*75ce41a5SAli Bahrami#
34*75ce41a5SAli Bahrami#	find_elf [-frs] path
35*75ce41a5SAli Bahrami#
36*75ce41a5SAli Bahrami# Where path is a file or directory.
37*75ce41a5SAli Bahrami#
38*75ce41a5SAli Bahrami# Each line of output is of the form:
39*75ce41a5SAli Bahrami#
40*75ce41a5SAli Bahrami#	ELFCLASS  ELFTYPE VERDEF|NOVERDEF relpath
41*75ce41a5SAli Bahrami#
42*75ce41a5SAli Bahrami# where relpath is the path relative to the directory from which the
43*75ce41a5SAli Bahrami# search started.
44*75ce41a5SAli Bahrami
45*75ce41a5SAli Bahramiuse strict;
46*75ce41a5SAli Bahrami
47*75ce41a5SAli Bahramiuse vars  qw($Prog %Output @SaveArgv);
48*75ce41a5SAli Bahramiuse vars  qw(%opt $HaveElfedit);
49*75ce41a5SAli Bahrami
50*75ce41a5SAli Bahrami# Hashes used to detect aliases --- symlinks that reference a common file
51*75ce41a5SAli Bahrami#
52*75ce41a5SAli Bahrami#	id_hash - Maps the unique st_dev/st_ino pair to the real file
53*75ce41a5SAli Bahrami#	alias_hash - Maps symlinks to the real file they reference
54*75ce41a5SAli Bahrami#
55*75ce41a5SAli Bahramiuse vars  qw(%id_hash %alias_hash);
56*75ce41a5SAli Bahrami
57*75ce41a5SAli Bahramiuse POSIX qw(getenv);
58*75ce41a5SAli Bahramiuse Getopt::Std;
59*75ce41a5SAli Bahramiuse File::Basename;
60*75ce41a5SAli Bahrami
61*75ce41a5SAli Bahrami
62*75ce41a5SAli Bahrami## GetObjectInfo(path)
63*75ce41a5SAli Bahrami#
64*75ce41a5SAli Bahrami# Return a 3 element output array describing the object
65*75ce41a5SAli Bahrami# given by path. The elements of the array contain:
66*75ce41a5SAli Bahrami#
67*75ce41a5SAli Bahrami#	Index   Meaning
68*75ce41a5SAli Bahrami#	-----------------------------------------------
69*75ce41a5SAli Bahrami#	0	ELFCLASS of object (0 if not an ELF object)
70*75ce41a5SAli Bahrami#	1	Type of object (NONE if not an ELF object)
71*75ce41a5SAli Bahrami#	2	VERDEF if object defines versions, NOVERDEF otherwise
72*75ce41a5SAli Bahrami#
73*75ce41a5SAli Bahramisub GetObjectInfo {
74*75ce41a5SAli Bahrami	my $path = $_[0];
75*75ce41a5SAli Bahrami
76*75ce41a5SAli Bahrami	# If elfedit is available, we use it to obtain the desired information
77*75ce41a5SAli Bahrami	# by executing three commands in order, to produce a 0, 2, or 3
78*75ce41a5SAli Bahrami	# element output array.
79*75ce41a5SAli Bahrami	#
80*75ce41a5SAli Bahrami	#	Command                 Meaning
81*75ce41a5SAli Bahrami	#	-----------------------------------------------
82*75ce41a5SAli Bahrami	#	ehdr:ei_class		ELFCLASS of object
83*75ce41a5SAli Bahrami	#	ehdr:ei_e_type		Type of object
84*75ce41a5SAli Bahrami	#	dyn:tag verdef		Address of verdef items
85*75ce41a5SAli Bahrami	#
86*75ce41a5SAli Bahrami	# We discard stderr, and simply examine the resulting array to
87*75ce41a5SAli Bahrami	# determine the situation:
88*75ce41a5SAli Bahrami	#
89*75ce41a5SAli Bahrami	#	# Array Elements	Meaning
90*75ce41a5SAli Bahrami	#	-----------------------------------------------
91*75ce41a5SAli Bahrami	#	  0			File is not ELF object
92*75ce41a5SAli Bahrami	#	  2			Object with no versions (no VERDEF)
93*75ce41a5SAli Bahrami	#	  3			Object that has versions
94*75ce41a5SAli Bahrami	if ($HaveElfedit) {
95*75ce41a5SAli Bahrami		my $ecmd = "elfedit -r -o simple -e ehdr:ei_class " .
96*75ce41a5SAli Bahrami		    "-e ehdr:e_type -e 'dyn:tag verdef'";
97*75ce41a5SAli Bahrami		my @Elf = split(/\n/, `$ecmd $path 2>/dev/null`);
98*75ce41a5SAli Bahrami
99*75ce41a5SAli Bahrami		my $ElfCnt = scalar @Elf;
100*75ce41a5SAli Bahrami
101*75ce41a5SAli Bahrami		# Return ET_NONE array if not an ELF object
102*75ce41a5SAli Bahrami		return (0, 'NONE', 'NOVERDEF') if ($ElfCnt == 0);
103*75ce41a5SAli Bahrami
104*75ce41a5SAli Bahrami		# Otherwise, convert the result to standard form
105*75ce41a5SAli Bahrami		$Elf[0] =~ s/^ELFCLASS//;
106*75ce41a5SAli Bahrami		$Elf[1] =~ s/^ET_//;
107*75ce41a5SAli Bahrami		$Elf[2] = ($ElfCnt == 3) ? 'VERDEF' : 'NOVERDEF';
108*75ce41a5SAli Bahrami		return @Elf;
109*75ce41a5SAli Bahrami	}
110*75ce41a5SAli Bahrami
111*75ce41a5SAli Bahrami	# For older platforms, we use elfdump to get the desired information.
112*75ce41a5SAli Bahrami	my @Elf = split(/\n/, `elfdump -ed $path 2>&1`);
113*75ce41a5SAli Bahrami	my $Header = 'None';
114*75ce41a5SAli Bahrami	my $Verdef = 'NOVERDEF';
115*75ce41a5SAli Bahrami	my ($Class, $Type);
116*75ce41a5SAli Bahrami
117*75ce41a5SAli Bahrami	foreach my $Line (@Elf) {
118*75ce41a5SAli Bahrami		# If we have an invalid file type (which we can tell from the
119*75ce41a5SAli Bahrami		# first line), or we're processing an archive, bail.
120*75ce41a5SAli Bahrami		if ($Header eq 'None') {
121*75ce41a5SAli Bahrami			if (($Line =~ /invalid file/) ||
122*75ce41a5SAli Bahrami			    ($Line =~ /$path(.*):/)) {
123*75ce41a5SAli Bahrami				return (0, 'NONE', 'NOVERDEF');
124*75ce41a5SAli Bahrami			}
125*75ce41a5SAli Bahrami		}
126*75ce41a5SAli Bahrami
127*75ce41a5SAli Bahrami		if ($Line =~ /^ELF Header/) {
128*75ce41a5SAli Bahrami			$Header = 'Ehdr';
129*75ce41a5SAli Bahrami			next;
130*75ce41a5SAli Bahrami		}
131*75ce41a5SAli Bahrami
132*75ce41a5SAli Bahrami		if ($Line =~ /^Dynamic Section/) {
133*75ce41a5SAli Bahrami			$Header = 'Dyn';
134*75ce41a5SAli Bahrami			next;
135*75ce41a5SAli Bahrami		}
136*75ce41a5SAli Bahrami
137*75ce41a5SAli Bahrami		if ($Header eq 'Ehdr') {
138*75ce41a5SAli Bahrami			if ($Line =~ /e_type:\s*ET_([^\s]+)/) {
139*75ce41a5SAli Bahrami				$Type = $1;
140*75ce41a5SAli Bahrami				next;
141*75ce41a5SAli Bahrami			}
142*75ce41a5SAli Bahrami			if ($Line =~ /ei_class:\s+ELFCLASS(\d+)/) {
143*75ce41a5SAli Bahrami				$Class = $1;
144*75ce41a5SAli Bahrami				next;
145*75ce41a5SAli Bahrami			}
146*75ce41a5SAli Bahrami			next;
147*75ce41a5SAli Bahrami		}
148*75ce41a5SAli Bahrami
149*75ce41a5SAli Bahrami		if (($Header eq 'Dyn') &&
150*75ce41a5SAli Bahrami		    ($Line =~ /^\s*\[\d+\]\s+VERDEF\s+/)) {
151*75ce41a5SAli Bahrami			$Verdef = 'VERDEF';
152*75ce41a5SAli Bahrami			next;
153*75ce41a5SAli Bahrami		}
154*75ce41a5SAli Bahrami	}
155*75ce41a5SAli Bahrami	return ($Class, $Type, $Verdef);
156*75ce41a5SAli Bahrami}
157*75ce41a5SAli Bahrami
158*75ce41a5SAli Bahrami
159*75ce41a5SAli Bahrami## ProcFile(FullPath, RelPath, AliasedPath, IsSymLink, dev, ino)
160*75ce41a5SAli Bahrami#
161*75ce41a5SAli Bahrami# Determine whether this a ELF dynamic object and if so, add a line
162*75ce41a5SAli Bahrami# of output for it to @Output describing it.
163*75ce41a5SAli Bahrami#
164*75ce41a5SAli Bahrami# entry:
165*75ce41a5SAli Bahrami#	FullPath - Fully qualified path
166*75ce41a5SAli Bahrami#	RelPath - Path relative to starting root directory
167*75ce41a5SAli Bahrami#	AliasedPath - True if RelPath contains a symlink directory component.
168*75ce41a5SAli Bahrami#		Such a path represents an alias to the same file found
169*75ce41a5SAli Bahrami#		completely via actual directories.
170*75ce41a5SAli Bahrami#	IsSymLink - True if basename (final component) of path is a symlink.
171*75ce41a5SAli Bahrami#
172*75ce41a5SAli Bahramisub ProcFile {
173*75ce41a5SAli Bahrami	my($FullPath, $RelPath, $AliasedPath, $IsSymLink, $dev, $ino) = @_;
174*75ce41a5SAli Bahrami	my(@Elf, @Pvs, @Pvs_don, @Vers, %TopVer);
175*75ce41a5SAli Bahrami	my($Aud, $Max, $Priv, $Pub, $ElfCnt, $Val, $Ttl, $NotPlugin);
176*75ce41a5SAli Bahrami
177*75ce41a5SAli Bahrami	my $uniqid = sprintf("%llx-%llx", $dev, $ino);
178*75ce41a5SAli Bahrami
179*75ce41a5SAli Bahrami	# Remove ./ from front of relative path
180*75ce41a5SAli Bahrami	$RelPath =~ s/^\.\///;
181*75ce41a5SAli Bahrami
182*75ce41a5SAli Bahrami	my $name = $opt{r} ? $RelPath : $FullPath;
183*75ce41a5SAli Bahrami
184*75ce41a5SAli Bahrami	# If this is a symlink, or the path contains a symlink, put it in
185*75ce41a5SAli Bahrami	# the alias hash for later analysis. We do this before testing to
186*75ce41a5SAli Bahrami	# see if it is an ELF file, because that's a relatively expensive
187*75ce41a5SAli Bahrami	# test. The tradeoff is that the alias hash will contain some files
188*75ce41a5SAli Bahrami	# we don't care about. That is a small cost.
189*75ce41a5SAli Bahrami	if ($IsSymLink || $AliasedPath) {
190*75ce41a5SAli Bahrami		$alias_hash{$name} = $uniqid;
191*75ce41a5SAli Bahrami		return;
192*75ce41a5SAli Bahrami	}
193*75ce41a5SAli Bahrami
194*75ce41a5SAli Bahrami	# Obtain the ELF information for this object.
195*75ce41a5SAli Bahrami	@Elf = GetObjectInfo($FullPath);
196*75ce41a5SAli Bahrami
197*75ce41a5SAli Bahrami        # Return quietly if:
198*75ce41a5SAli Bahrami	#	- Not an executable or sharable object
199*75ce41a5SAli Bahrami	#	- An executable, but the -s option was used.
200*75ce41a5SAli Bahrami	if ((($Elf[1] ne 'EXEC') && ($Elf[1] ne 'DYN')) ||
201*75ce41a5SAli Bahrami	    (($Elf[1] eq 'EXEC') && $opt{s})) {
202*75ce41a5SAli Bahrami		return;
203*75ce41a5SAli Bahrami	}
204*75ce41a5SAli Bahrami
205*75ce41a5SAli Bahrami	$Output{$name} = sprintf("OBJECT %2s %-4s %-8s %s\n",
206*75ce41a5SAli Bahrami	    $Elf[0], $Elf[1], $Elf[2], $name);
207*75ce41a5SAli Bahrami
208*75ce41a5SAli Bahrami	# Remember it for later alias analysis
209*75ce41a5SAli Bahrami	$id_hash{$uniqid} = $name;
210*75ce41a5SAli Bahrami}
211*75ce41a5SAli Bahrami
212*75ce41a5SAli Bahrami
213*75ce41a5SAli Bahrami## ProcDir(FullPath, RelPath, AliasedPath, SelfSymlink)
214*75ce41a5SAli Bahrami#
215*75ce41a5SAli Bahrami# Recursively search directory for dynamic ELF objects, calling
216*75ce41a5SAli Bahrami# ProcFile() on each one.
217*75ce41a5SAli Bahrami#
218*75ce41a5SAli Bahrami# entry:
219*75ce41a5SAli Bahrami#	FullPath - Fully qualified path
220*75ce41a5SAli Bahrami#	RelPath - Path relative to starting root directory
221*75ce41a5SAli Bahrami#	AliasedPath - True if RelPath contains a symlink directory component.
222*75ce41a5SAli Bahrami#		Such a path represents an alias to the same file found
223*75ce41a5SAli Bahrami#		completely via actual directories.
224*75ce41a5SAli Bahrami#	SelfSymlink - True (1) if the last segment in the path is a symlink
225*75ce41a5SAli Bahrami#		that points at the same directory (i.e. 32->.). If SelfSymlink
226*75ce41a5SAli Bahrami#		is True, ProcDir() examines the given directory for objects,
227*75ce41a5SAli Bahrami#		but does not recurse past it. This captures the aliases for
228*75ce41a5SAli Bahrami#		those objects, while avoiding entering a recursive loop,
229*75ce41a5SAli Bahrami#		or generating nonsensical paths (i.e., 32/amd64/...).
230*75ce41a5SAli Bahrami#
231*75ce41a5SAli Bahramisub ProcDir {
232*75ce41a5SAli Bahrami	my($FullDir, $RelDir, $AliasedPath, $SelfSymlink) = @_;
233*75ce41a5SAli Bahrami	my($NewFull, $NewRel, $Entry);
234*75ce41a5SAli Bahrami
235*75ce41a5SAli Bahrami	# Open the directory and read each entry, omit files starting with "."
236*75ce41a5SAli Bahrami	if (opendir(DIR, $FullDir)) {
237*75ce41a5SAli Bahrami		foreach $Entry (readdir(DIR)) {
238*75ce41a5SAli Bahrami
239*75ce41a5SAli Bahrami			if ($Entry =~ /^\./) {
240*75ce41a5SAli Bahrami				next;
241*75ce41a5SAli Bahrami			}
242*75ce41a5SAli Bahrami			$NewFull = join('/', $FullDir, $Entry);
243*75ce41a5SAli Bahrami
244*75ce41a5SAli Bahrami			# We need to follow symlinks in order to capture
245*75ce41a5SAli Bahrami			# all possible aliases for each object. However,
246*75ce41a5SAli Bahrami			# symlinks that point back at the same directory
247*75ce41a5SAli Bahrami			# (e.g. 32->.) must be flagged via the SelfSymlink
248*75ce41a5SAli Bahrami			# argument to our recursive self in order to avoid
249*75ce41a5SAli Bahrami			# taking it more than one level down.
250*75ce41a5SAli Bahrami			my $RecurseAliasedPath = $AliasedPath;
251*75ce41a5SAli Bahrami			my $RecurseSelfSymlink = 0;
252*75ce41a5SAli Bahrami			my $IsSymLink = -l $NewFull;
253*75ce41a5SAli Bahrami			if ($IsSymLink) {
254*75ce41a5SAli Bahrami				my $trans = readlink($NewFull);
255*75ce41a5SAli Bahrami
256*75ce41a5SAli Bahrami				$trans =~ s/\/*$//;
257*75ce41a5SAli Bahrami				$RecurseSelfSymlink = 1 if $trans eq '.';
258*75ce41a5SAli Bahrami				$RecurseAliasedPath = 1;
259*75ce41a5SAli Bahrami			}
260*75ce41a5SAli Bahrami
261*75ce41a5SAli Bahrami			if (!stat($NewFull)) {
262*75ce41a5SAli Bahrami				next;
263*75ce41a5SAli Bahrami			}
264*75ce41a5SAli Bahrami			$NewRel = join('/', $RelDir, $Entry);
265*75ce41a5SAli Bahrami
266*75ce41a5SAli Bahrami			# Descend into and process any directories.
267*75ce41a5SAli Bahrami			if (-d _) {
268*75ce41a5SAli Bahrami				# If we have recursed here via a $SelfSymlink,
269*75ce41a5SAli Bahrami				# then do not persue directories. We only
270*75ce41a5SAli Bahrami				# want to find objects in the same directory
271*75ce41a5SAli Bahrami				# via that link.
272*75ce41a5SAli Bahrami				next if $SelfSymlink;
273*75ce41a5SAli Bahrami
274*75ce41a5SAli Bahrami				ProcDir($NewFull, $NewRel, $RecurseAliasedPath,
275*75ce41a5SAli Bahrami				    $RecurseSelfSymlink);
276*75ce41a5SAli Bahrami				next;
277*75ce41a5SAli Bahrami			}
278*75ce41a5SAli Bahrami
279*75ce41a5SAli Bahrami			# In fast mode, we skip objects unless they end with
280*75ce41a5SAli Bahrami			# a .so extension, or are executable. We touch
281*75ce41a5SAli Bahrami			# considerably fewer files this way.
282*75ce41a5SAli Bahrami			if ($opt{f} && !($Entry =~ /\.so$/) &&
283*75ce41a5SAli Bahrami			    !($Entry =~ /\.so\./) &&
284*75ce41a5SAli Bahrami			    ($opt{s} || (! -x _))) {
285*75ce41a5SAli Bahrami			    next;
286*75ce41a5SAli Bahrami			}
287*75ce41a5SAli Bahrami
288*75ce41a5SAli Bahrami			# Process any standard files.
289*75ce41a5SAli Bahrami			if (-f _) {
290*75ce41a5SAli Bahrami				my ($dev, $ino) = stat(_);
291*75ce41a5SAli Bahrami				ProcFile($NewFull, $NewRel, $AliasedPath,
292*75ce41a5SAli Bahrami				    $IsSymLink, $dev, $ino);
293*75ce41a5SAli Bahrami				next;
294*75ce41a5SAli Bahrami			}
295*75ce41a5SAli Bahrami
296*75ce41a5SAli Bahrami		}
297*75ce41a5SAli Bahrami		closedir(DIR);
298*75ce41a5SAli Bahrami	}
299*75ce41a5SAli Bahrami}
300*75ce41a5SAli Bahrami
301*75ce41a5SAli Bahrami
302*75ce41a5SAli Bahrami# -----------------------------------------------------------------------------
303*75ce41a5SAli Bahrami
304*75ce41a5SAli Bahrami# Establish a program name for any error diagnostics.
305*75ce41a5SAli Bahramichomp($Prog = `basename $0`);
306*75ce41a5SAli Bahrami
307*75ce41a5SAli Bahrami# The onbld_elfmod package is maintained in the same directory as this
308*75ce41a5SAli Bahrami# script, and is installed in ../lib/perl. Use the local one if present,
309*75ce41a5SAli Bahrami# and the installed one otherwise.
310*75ce41a5SAli Bahramimy $moddir = dirname($0);
311*75ce41a5SAli Bahrami$moddir = "$moddir/../lib/perl" if ! -f "$moddir/onbld_elfmod.pm";
312*75ce41a5SAli Bahramirequire "$moddir/onbld_elfmod.pm";
313*75ce41a5SAli Bahrami
314*75ce41a5SAli Bahrami# Check that we have arguments.
315*75ce41a5SAli Bahrami@SaveArgv = @ARGV;
316*75ce41a5SAli Bahramiif ((getopts('frs', \%opt) == 0) || (scalar(@ARGV) != 1)) {
317*75ce41a5SAli Bahrami	print "usage: $Prog [-frs] file | dir\n";
318*75ce41a5SAli Bahrami	print "\t[-f]\tuse file name at mode to speed search\n";
319*75ce41a5SAli Bahrami	print "\t[-r]\treport relative paths\n";
320*75ce41a5SAli Bahrami	print "\t[-s]\tonly remote sharable (ET_DYN) objects\n";
321*75ce41a5SAli Bahrami	exit 1;
322*75ce41a5SAli Bahrami}
323*75ce41a5SAli Bahrami
324*75ce41a5SAli Bahrami%Output = ();
325*75ce41a5SAli Bahrami%id_hash = ();
326*75ce41a5SAli Bahrami%alias_hash = ();
327*75ce41a5SAli Bahrami$HaveElfedit = -x '/usr/bin/elfedit';
328*75ce41a5SAli Bahrami
329*75ce41a5SAli Bahramimy $Arg = $ARGV[0];
330*75ce41a5SAli Bahramimy $Error = 0;
331*75ce41a5SAli Bahrami
332*75ce41a5SAli BahramiARG: {
333*75ce41a5SAli Bahrami	# Process simple files.
334*75ce41a5SAli Bahrami	if (-f $Arg) {
335*75ce41a5SAli Bahrami		my($RelPath) = $Arg;
336*75ce41a5SAli Bahrami
337*75ce41a5SAli Bahrami		if ($opt{r}) {
338*75ce41a5SAli Bahrami			my $Prefix = $Arg;
339*75ce41a5SAli Bahrami
340*75ce41a5SAli Bahrami			$Prefix =~ s/(^.*)\/.*$/$1/;
341*75ce41a5SAli Bahrami			$Prefix = '.' if ($Prefix eq $Arg);
342*75ce41a5SAli Bahrami			print "PREFIX $Prefix\n";
343*75ce41a5SAli Bahrami		}
344*75ce41a5SAli Bahrami		$RelPath =~ s/^.*\//.\//;
345*75ce41a5SAli Bahrami		my ($dev, $ino) = stat(_);
346*75ce41a5SAli Bahrami		my $IsSymLink = -l $Arg;
347*75ce41a5SAli Bahrami		ProcFile($Arg, $RelPath, 0, $IsSymLink, $dev, $ino);
348*75ce41a5SAli Bahrami		next;
349*75ce41a5SAli Bahrami	}
350*75ce41a5SAli Bahrami
351*75ce41a5SAli Bahrami	# Process directories.
352*75ce41a5SAli Bahrami	if (-d $Arg) {
353*75ce41a5SAli Bahrami		$Arg =~ s/\/$//;
354*75ce41a5SAli Bahrami		print "PREFIX $Arg\n" if $opt{r};
355*75ce41a5SAli Bahrami		ProcDir($Arg, ".", 0, 0);
356*75ce41a5SAli Bahrami		next;
357*75ce41a5SAli Bahrami	}
358*75ce41a5SAli Bahrami
359*75ce41a5SAli Bahrami	print "$Arg is not a file or directory\n";
360*75ce41a5SAli Bahrami	$Error = 1;
361*75ce41a5SAli Bahrami}
362*75ce41a5SAli Bahrami
363*75ce41a5SAli Bahrami# Build a hash, using the primary file name as the key, that has the
364*75ce41a5SAli Bahrami# strings for any aliases to that file.
365*75ce41a5SAli Bahramimy %alias_text = ();
366*75ce41a5SAli Bahramiforeach my $Alias (sort keys %alias_hash) {
367*75ce41a5SAli Bahrami	my $id = $alias_hash{$Alias};
368*75ce41a5SAli Bahrami	if (defined($id_hash{$id})) {
369*75ce41a5SAli Bahrami		my $obj = $id_hash{$id};
370*75ce41a5SAli Bahrami		my $str = "ALIAS                   $id_hash{$id}\t$Alias\n";
371*75ce41a5SAli Bahrami
372*75ce41a5SAli Bahrami		if (defined($alias_text{$obj})) {
373*75ce41a5SAli Bahrami			$alias_text{$obj} .= $str;
374*75ce41a5SAli Bahrami		} else {
375*75ce41a5SAli Bahrami			$alias_text{$obj} = $str;
376*75ce41a5SAli Bahrami		}
377*75ce41a5SAli Bahrami	}
378*75ce41a5SAli Bahrami}
379*75ce41a5SAli Bahrami
380*75ce41a5SAli Bahrami# Output the main files sorted by name. Place the alias lines immediately
381*75ce41a5SAli Bahrami# following each main file.
382*75ce41a5SAli Bahramiforeach my $Path (sort keys %Output) {
383*75ce41a5SAli Bahrami	print $Output{$Path};
384*75ce41a5SAli Bahrami	print $alias_text{$Path} if defined($alias_text{$Path});
385*75ce41a5SAli Bahrami}
386*75ce41a5SAli Bahrami
387*75ce41a5SAli Bahramiexit $Error;
388