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 = ) { $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;