summaryrefslogtreecommitdiff
path: root/usr/src/tools/scripts/onbld_elfmod.pm
blob: 7754cf901277822186efbe70e6c70c8ab061e1b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
package onbld_elfmod;

#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# This perl module contains code shared between the ELF analysis
# tools found in this directory: find_elf, check_rtime, interface_check,
# and interface_cmp.
#

use strict;
use File::Basename;

## GetLine(FileHandleRef, LineNumRef)
#
# Read the next non-empty line from the given file handle reference
# and return it.
#
# entry:
#	FileHandleRef - Reference to open file handle to read from
#	LineNumRef - Reference to integer to increment as lines are input
#
sub GetLine {
	my ($fh, $LineNum) = @_;
	my $ret_line = '';
	my $line;
	my $cont = 1;

	while ($cont && ($line = <$fh>)) {
		$$LineNum++;
		chomp $line;

		# A backslash at the end of the line indicates that the
		# following line is a continuation of this line if the
		# backslash is the only character on the line, or if it is
		# preceded by a space.
		next if ($line eq '\\');
		$cont = ($line =~ s/\s+\\$//);

		# The # character starts a comment if it is the first
		# character on the line, or if it is preceeded by a space.
		if ($line =~ /^\#/) {
			$cont = 1;
			next;
		}
		$line =~ s/\s+\#.*$//;		# Strip Comments
		$line =~ s/\s*$//;		# Trailing whitespace

		if ($line !~ /^\s*$/) {		# Non-empty string
			$line =~ s/^\s+//;	# Leading whitespace
			if ($ret_line eq '') {
				$ret_line = $line;
			} else {
				$ret_line = "$ret_line $line";
			}
		}

		# If our result string is still null, act as if a 
		# continuation is present and read another line.
		$cont = 1 if ($ret_line eq '');
	}

	# The above loop won't exit while $ret_line is a null string
	# unless the read failed, so return undef() in that case.
	# Otherwise, use the value in $ret_line.
	return ($ret_line ne '') ? $ret_line : undef();
}


## LoadExceptionsToEXRE(name)
#
# Locate the exceptions file and process its contents. This function can be
# used by any program with exception files that consist of a single
# verb, followed by a single regular expression:
#
#	VERB regex
#
# For each such verb, the global level of the main:: namespace must
# have a variable named $EXRE_verb. The $EXRE_ prefix must only be used
# for these variables, and not for any other. The caller must define these
# variables, but leave them undefined.
#
# entry:
#	Any variables in the main:: global symbol table starting with
#	the prefix 'EXRE_xxx' are taken to represent the regular expression
#	for the exception named xxx.
#
#	name - Name of script (i.e. 'check_rtime')
#	$main::opt{e} - Calling program must accept a '-e' option
#		that allows the user to specify an exception file
#		to use, and the value of that option must be found
#		in $main::opt{e}.
#
# exit:
#	The $main::EXRE_xxx variables are updated to contain any regular
#	expressions specified by the exception file. If a given exception
#	is not encountered, its variable is not modified.
#
# note:
#	We expand strings of the form MACH(dir) to match the given
#	directory as well as any 64-bit architecture subdirectory that
#	might be present (i.e. amd64, sparcv9).
# 
sub LoadExceptionsToEXRE {
	my $name = $_[0];
	my $file;
	my $Line;
	my $LineNum = 0;
	my $err = 0;
	my %except_names = ();
	my %except_re = ();

	# Examine the main global symbol table and find all variables
	# named EXRE_xxx. By convention established for this program,
	# all such variables contain the regular expression for the
	# exception named xxx.
	foreach my $entry (keys %main::) {
		$except_names{$entry} = 1 if $entry =~ /^EXRE_/;
	}

	# Locate the exception file
	FILE: {
		# If -e is specified, that file must be used
		if ($main::opt{e}) {
			$file = $main::opt{e};
			last FILE;
		}

		# If this is an activated workspace, use the exception
		# file found in the exceptions_list directory.
		if (defined($ENV{CODEMGR_WS})) {
			$file = "$ENV{CODEMGR_WS}/exception_lists/$name";
			last FILE if (-f $file);
		}

		# As a final backstop, the SUNWonbld package provides a
		# copy of the exception file. This can be useful if we
		# are being used with an older workspace.
		#
		# This script is installed in the SUNWonbld bin directory,
		# while the exception file is in etc/exception_lists. Find
		# it relative to the script location given by $0.
		$file = dirname($0) . "/../etc/exception_lists/$name";
		last FILE if (-f $file);

		# No exception file was found.
		return;
	}

	open (EFILE, $file) ||
		die "$name: unable to open exceptions file: $file";
	while ($Line = onbld_elfmod::GetLine(\*EFILE, \$LineNum)) {
		# Expand MACH()
		$Line =~ s/MACH\(([^)]+)\)/$1(\/amd64|\/sparcv9)?/;

		# %except_re is a hash indexed by regular expression variable
		# name, with a value that contains the corresponding regular
		# expression string. If we recognize an exception verb, add
		# it to %except_re.
		if ($Line =~ /^\s*([^\s]+)\s+(.*)$/i) {
			my $verb = $1;
			my $re = $2;

			$verb =~ tr/A-Z/a-z/;
			$verb = "EXRE_$verb";
			if ($except_names{$verb}) {
				if (defined($except_re{$verb})) {
					$except_re{$verb} .= '|' . $re;
				} else {
					$except_re{$verb} = $re;
				}
			}
			next;
		}

		$err++;
		printf(STDERR "$file: Unrecognized option: ".
		    "line $LineNum: $Line\n");
	}
	close EFILE;

	# Every exception that we encountered in the file exists
	# in %except_re. Compile them and assign the results into the
	# global symbol of the same name.
	#
	# Note that this leaves the global symbols for unused exceptions
	# untouched, and therefore, undefined. All users of these variables
	# are required to test them with defined() before using them.
	foreach my $verb (sort keys %except_names) {
		next if !defined($except_re{$verb});

		# Turn off strict refs so that we can do a symbolic
		# indirection to set the global variable of the name given
		# by verb in the main namespace. 'strict' is lexically scoped,
		# so its influence is limited to this enclosing block.
		no strict 'refs';
		${"main::$verb"} = qr/$except_re{$verb}/;
	}

	exit 1 if ($err != 0);
}


## OutMsg(FileHandleRef, Ttl, obj, msg)
## OutMsg2(FileHandleRef, Ttl, old_obj, new_obj, msg)
#
# Create an output message, either a one-liner (under -o) or preceded by the
# files relative pathname as a title.
#
# OutMsg() is used when issuing a message about a single object.
#
# OutMsg2() is for when the message involves an old and new instance
# of the same object. If old_obj and new_obj are the same, as is usually
# the case, then the output is the same as generated by OutMsg(). If they
# differ, as can happen when the new object has changed names, and has been
# found via an alias, both the old and new names are shown.
#
# entry:
#	FileHandleRef - File handle to output file
#	Ttl - Reference to variable containing the number of times
#		this function has been called for the current object.
#	obj - For OutMsg, the path for the current object
#	old_obj, new_obj - For OutMsg2, the names of the "old" and "new"
#		objects.
#	msg - Message to output
#
#	$main::opt{o} - Calling program must accept a '-o' option
#		that allows the user to specify "one-line-mode',
#		and the value of that option must be found
#		in $main::opt{o}.
#
sub OutMsg {
	my($fh, $Ttl, $obj, $msg) = @_;

	if ($main::opt{o}) {
		print $fh "$obj: $msg\n";
	} else {
		print $fh "==== $obj ====\n" if ($$Ttl++ eq 0);
		print $fh "\t$msg\n";
	}
}

sub OutMsg2 {
	my ($fh, $Ttl, $old_obj, $new_obj, $msg) = @_;

	# If old and new are the same, give it to OutMsg()
	if ($old_obj eq $new_obj) {
		OutMsg($fh, $Ttl, $old_obj, $msg);
		return;
	}

	if ($main::opt{o}) {
		print "old $old_obj: new $new_obj: $msg\n";
	} else {
		print "==== old: $old_obj / new: $new_obj ====\n"
		    if ($$Ttl++ eq 0);
		print "\t$msg\n";
	}
}


## header(FileHandleRef, ScriptPath, Argv)
#
# Generate a header for the top of generated output, including a copyright
# and CDDL, such that the file will pass ON copyright/CDDL rules if it is
# checked into the repository.
#
# entry:
#	FileHandleRef - File handle reference to output text to
#	ScriptPath - Value of $0 from caller, giving path to running script
#	Argv - Reference to array containing @ARGV from caller.
#
# note:
#	We assume that the calling script contains a value CDDL block.
#
sub Header {

	my ($fh, $ScriptPath, $Argv) = @_;
	my $year = 1900 + (localtime())[5];

	print $fh "#\n";
	print $fh "# Copyright $year Sun Microsystems, Inc.  ",
	    "All rights reserved.\n";
	print $fh "# Use is subject to license terms.\n#\n";

	# The CDDL text is copied from this script, the path to which is
	# assigned to $0 by the Perl interpreter.
	if (open(CDDL, $ScriptPath)) {
		my $out = 0;
		my $Line;

		while ($Line = <CDDL>) {
			$out = 1 if ($Line =~ /^\# CDDL HEADER START/);

			print $fh $Line if $out;
			last if ($Line =~ /^\# CDDL HEADER END/);
		}
		print $fh "#\n\n";
		close CDDL;
	}

	print $fh '# Date:    ', scalar(localtime()), "\n";
	$ScriptPath =~ s/^.*\///;
	$ScriptPath =~ s/\.pl$//;
	print $fh "# Command: $ScriptPath ", join(' ', @$Argv), "\n\n";
}

# Perl modules pulled in via 'require' must return an exit status.
1;