interface_check.pl revision 75ce41a57ff334bd8fe2cb9ed51eea835892f944
#
# 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
# 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.
#
#
# Check versioning information.
#
# This script descends a directory hierarchy inspecting ELF shared objects for
# version definitions. The general theme is to verify that common versioning
# rules have been used to build these objects.
#
# As always, a number of components don't follow the rules, or require
# special handling. An exceptions file is used to specify these cases.
#
# By default any file that has conditions that should be reported is first
# listed and then each condition follows. The -o (one-line) option produces a
#
# Besides the default operation of checking the files within a directory
# hierarchy, a detailed analysis of each files versions can be created with the
# -d option. The database created is useful for auditing the difference between
# different builds, and for thus monitoring that versioning changes are made in
# a compatible manner.
# Define all global variables (required for strict)
# An exception file is used to specify regular expressions to match
# objects. These directives specify special attributes of the object.
# The regular expressions are read from the file and compiled into the
# regular expression variables.
#
# The name of each regular expression variable is of the form
#
# $EXRE_xxx
#
# where xxx is the name of the exception in lower case. For example,
# the regular expression variable for PLUGINS is $EXRE_plugins.
#
# onbld_elfmod::LoadExceptionsToEXRE() depends on this naming convention
# to initialize the regular expression variables, and to detect invalid
# exception names.
#
# If a given exception is not used in the exception file, its regular
# expression variable will be undefined. Users of these variables must
# test the variable with defined() prior to use:
#
# defined($EXRE_plugins) && ($foo =~ $EXRE_plugins)
#
# ----
#
# The exceptions are:
#
# NONSTD_VERNAME
# Objects are expected to use standard names for versions.
# This directive is used to relax that requirement.
#
# NOVERDEF
# Objects that are not required to have a versioned name. Note that
# PLUGINS objects are implicitly NOVERDEF, so this directive is
# for use with non-plugin objects.
#
# PLUGINS
# Plugin objects are not required to have a versioned name, and are
# not required to be internally versioned.
#
use strict;
## ProcFile(BasePath, RelPath, Class, Type, Verdef, Alias)
#
# Investigate runtime attributes of a sharable object
#
# entry:
# BasePath - Base path from which relative paths are taken
# RelPath - Path of object taken relative to BasePath
# Class - ELFCLASS of object
# Type - ELF type of object
# Verdef - VERDEF if object defines versions, NOVERDEF otherwise
# Alias - Alias lines corresponding to the object, or an empty ('')
# string if there are no aliases.
#
sub ProcFile {
$FullPath = "$BasePath/$RelPath";
@_ = split /\//, $RelPath;
$File = $_[$#_];
$Ttl = 0;
# If this object does not follow the runtime versioned name convention,
# and it does not reside underneath a directory identified as
# containing plugin objects intended for use with dlopen() only,
# issue a warning.
"does not have a versioned name");
}
# If there are no versions in the file we're done.
if ($Verdef eq 'NOVERDEF') {
# Report the lack of versioning, unless the object is
# a known plugin, or is explicitly exempt.
if ($NotPlugin &&
"no versions found");
}
return;
}
# Get a hash of the top versions in the inheritance chains.
%TopVer = ();
$Line =~ s/^.*-\s*(.*);/$1/;
}
# First determine what versions exist that offer interfaces. pvs -dos
# will list these. Note that other versions may exist, ones that
# don't offer interfaces ... we'll get to those next.
%Vers = ();
$VersCnt = 0;
my %TopSUNWVers = ();
# See if we've already caught this version name. We only look
# at each version once.
# Note that the non-empty version has been seen
$VersCnt++;
# We expect the public SUNW_major.minor.micro versions to use
# inheritance, so there should only be one top version for
# each major number. It is possible, though rare, to have
# more than one top version if the major numbers differ.
#
# %TopSUNWVers uses the major name as the key, with each
# value yielding an array reference to the top versions for
# that major number.
next;
}
# Having already handled SUNW_ public versions above, is it
# a different version name that we recognise?
#
# Along with the standard version names, each object exports
# a "base" version which contains the linker generated symbols
# _etext, _edata, etc., and is named using the objects SONAME.
# This name should typically match the file name.
# If we get here, it's a non-standard version.
if (!defined($EXRE_nonstd_vername) ||
($RelPath !~ $EXRE_nonstd_vername)) {
"non-standard version name: $Ver");
}
next;
}
# If this file has been scoped, but not versioned (i.e., a mapfile was
# used to demote symbols but no version name was applied to the
# global interfaces) then it's another non-standard case.
if ($VersCnt eq 0) {
"scoped object contains no versions");
return;
}
# If this file has multiple inheritance chains with the public
# SUNW_ name, that's wrong.
foreach my $Ver (sort keys %TopSUNWVers) {
"multiple $Ver inheritance chains (missing " .
"inheritance?): " .
}
}
# Produce an interface description for the object.
# For each version, generate a VERSION declaration of the form:
#
# [TOP_]VERSION version direct-count total-count
# symname1
# symname2
# ...
#
# There are two types of version that we suppress from this
# output:
#
# BASE
# The "base" version is used to hold symbols that must be
# public, but which are not part of the versioning interface
# (_end, _GLOBAL_OFFSET_TABLE_, _PROCEDURE_LINKAGE_TABLE_, etc).
#
# Private
# Any version with "private" in its name is skipped. We
# expect these to be SUNWprivate, but are extra lenient in
# what we accept.
#
# If an object only has base or private versions, we do not produce
# an interface description for that object.
#
if ($opt{i}) {
my $header_done = 0;
# The use of 'pvs -v' is to identify the BASE version
# Skip base version
# Skip private versions
# Directly inherited versions follow the version name
# in a comma separated list within {} brackets. Capture
# that information, for use with our VERSION line.
$Line =~ s/^\s*([^;: ]*).*/$1/;
# Older versions of pvs have a bug that prevents
# them from printing [BASE] on the base version.
# Work around this by excluding versions that end
# with a '.so.*' suffix.
# SONAME of the object.
# We want to output the symbols in sorted order, so
# we gather them first, and then sort the results.
# An array would suffice, but we have observed objects
# with odd inheritance chains in which the same
# sub-version gets inherited more than once, leading
# to the same symbol showing up more than once. Using
# a hash instead of an array thins out the duplicates.
my %Syms = ();
my $version_cnt = 0;
foreach my $Sym
if ($Sym =~ /:$/) {
$version_cnt++;
# If this is an inherited sub-version,
# we don't need to continue unless
# generating output in -I mode.
if ($version_cnt >= 2) {
last if !$opt{I};
$symitem = 'INHERIT';
}
next;
}
$Sym =~ s/[ \t]*(.*);$/$1/;
$Sym =~ s/ .*$//; # remove any data size
}
if (!$header_done) {
$ObjCnt++;
print INTFILE "OBJECT\t$RelPath\n";
print INTFILE "CLASS\tELFCLASS$Class\n";
print INTFILE "TYPE\tET_$Type\n";
$header_done = 1;
}
print INTFILE "$item\t$Line$InheritVers\n";
# Output symbols in sorted order
print INTFILE "\t$Syms{$Sym}\t$Sym\n";
}
}
}
}
## ProcFindElf(file)
#
# Open the specified file, which must be produced by "find_elf -r",
# and process the files it describes.
sub ProcFindElf {
my $file = $_[0];
my $line;
my $LineNum = 0;
my $prefix;
my @ObjList = ();
my %ObjToAlias = ();
# This script requires relative paths, created by the 'find_elf -r'
# option. When this is done, the first non-comment line will always
# be PREFIX. Obtain that line, or issue a fatal error.
$prefix = $1;
last;
}
die "$file: PREFIX expected on line $LineNum\n";
}
# Process the remainder of the file.
next;
}
my $str = "ALIAS\t$alias\n";
if (defined($ObjToAlias{$obj})) {
} else {
}
}
}
split(/\s+/, $line, 5);
# We are only interested in sharable objects. We may see
# other file types if processing a list of objects
# supplied via the -f option.
next if ($type ne 'DYN');
}
close FIND_ELF;
}
# -----------------------------------------------------------------------------
# Establish a program name for any error diagnostics.
# The onbld_elfmod package is maintained in the same directory as this
# and the installed one otherwise.
require "$moddir/onbld_elfmod.pm";
# Check that we have arguments.
print "usage: $Prog [-hIo] [-E errfile] [-e exfile] [-f listfile]\n";
print "\t\t[-i intffile] [-w outdir] file | dir, ...\n";
print "\n";
print "\t[-E errfile]\tdirect error output to file\n";
print "\t[-e exfile]\texceptions file\n";
print "\t[-f listfile]\tuse file list produced by find_elf -r\n";
print "\t[-I]\tExpand inheritance in -i output (debugging)\n";
print "\t[-i intffile]\tcreate interface description output file\n";
print "\t[-o]\t\tproduce one-liner output (prefixed with pathname)\n";
print "\t[-w outdir]\tinterpret all files relative to given directory\n";
exit 1;
}
# If -w, change working directory to given location
# Error messages go to stdout unless -E is specified. $ErrFH is a
# file handle reference that points at the file handle where error messages
# are sent.
if ($opt{E}) {
} else {
}
# Locate and process the exceptions file
# If creating an interface description output file, prepare it for use
if ($opt{i}) {
open (INTFILE, ">$opt{i}") ||
die "$Prog: Unable to create file: $opt{i}";
# Generate the output header
}
# Number of OBJECTs output to INTFILE
$ObjCnt = 0;
# If we were passed a file previously produced by 'find_elf -r', use it.
# Process each argument
# Run find_elf to find the files given by $Arg and process them
ProcFindElf("find_elf -frs $Arg|");
}
# Close any working output files.
exit 0;