  #!/bin/sh -- # A comment mentioning perl.
eval 'exec perl -S $0 ${1+"$@"}'
	if 0;
#
# Copyright (c) 2001-2003 by Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# For more info run this script with -h or look at the help text near
# the bottom of this file.
#

require 5.000;

use strict;
use Getopt::Std;
use File::Basename;

# os type and global database hashes and arrays:
my ($OS, %Inodes, %Filter, %Private, %Evolving, %Override, %Deleted, %Skipsym);
my (%Lib_Dirs, %External_DB, %ABI_Libs, @Rerun_Args, @Check_List, %Cmds);

# library archive (.a) structures.
my (%AR_sym, %AR_cnt, %AR_lib, @Archive_Skip);

# regex patterns for matching and parsing output:
my ($binding_match, $filter_match, $unbound_match, $dtneeded_match);
my ($lddstub_match, $ld_debug_match);
my ($file_match, %lib_match);
my $lib_match = '';

# command option related variables:
my $find_all_libs = 0;			# -a option
my $check_libraries = 1;		# -I option
my $ld_path = undef;			# -L option
my $ld_path_all_libs = 0;

my $private_match  = q/(?i)private/;	# -p option
my $evolving_match = q/(?i)evolving/;	# -e option
my $extended_private_match = 0;
my $extended_evolving_match = 0;

my $private_db_file = '';		# -d option
my $private_db_only = 0;		# -D option
my $static_archive_file = '';		# -s option
my $static_only = 0;			# -S option
my $static_check = 1;
my $override_db_file = '';		# -O option
my $abi_library_file = '';		# -A option

my $output_file = '';			# -o option
my $njobs = 0;				# -j option

my $keep_going = 0;			# -k option
my $verbose = 0;			# -v option
my $insane_verbosity = 0;		# -V option (not yet in doc)

# globals for speed or signal handling:
my (@pids, @tmpfiles);			# arrays used in parallel mode

select(STDERR); $| = 1;
select(STDOUT); $| = 1;

#
# determine OS and setup OS specific things:
#
os_setup();

#
# process cmd line args:
#
get_options();

#
# run separate processes if instructed to:
#
if ( $njobs > 1 ) {
	parallel();
	exit 1;
}

#
# initialize various info and pattern matches:
#
data_setup();

#
# load external database from -s/-S, -d/-D, -O, -A options.
#
load_external_dbs();

#
# loop over all binary objects and check each one: 
#
my $problem_count = 0;
my $file;
foreach $file (@ARGV) {
	process($file);
}

if ( $find_all_libs || $ld_path_all_libs ) {
	#
	# This is for the -a and -L find options.  In process() we only
	# gathered all the libraries in the set to be tested into
	# lib_match.  Now we test each binary so calls from these
	# libraries will also be checked and/or substitue the "find"
	# string in LD_LIBRARY_PATH.
	#
	modify_ld_path() if $ld_path_all_libs;

	foreach $file (@Check_List) {
		check($file);
	}
}

if ( $problem_count > 0 ) {
	exit 2;
} else {
	exit 0;
}

###########################################################################
# subroutines

#
# Top level routine for processing one binary.
#

sub process {
	my($file) = @_;
	# we handle some obvious "skip" cases here:
	if ( ! -e $file ) {
		file_header($file);
		print "$file: SKIP: $!\n";
		return;
	}
	if ( ! -f $file ) {
		file_header($file);
		print "$file: SKIP: not a regular file\n";
		return;
	}
	if ( $OS eq 'linux' && (-u $file || -g $file) ) {
		file_header($file);
		print "$file: SKIP: file is SUID or SGID\n";
		return;
	}
	if ( $file =~ /['\n]/ ) {
		#
		# these characters will likely cause our calls to the shell
		# to fail, so we skip these (oddly named) files.
		#
		my $tmp = $file;
		$tmp =~ s/\n/\\n/g;

		file_header($file);
		print "$tmp: SKIP: bad character in filename\n";
		return;
	}
	if ( ! is_elf($file) ) {
		if ( ! -r $file ) {
			# it could be ELF it we could not read it.
			open(TMP, "<$file");
			file_header($file);
			print "$file: SKIP: cannot read: $!\n";
			close(TMP);
		} else {
			file_header($file);
			print "$file: SKIP: not an ELF file\n";
		}
		return;
	}

	if ( $find_all_libs || $ld_path_all_libs ) {
		#
		# we just collect the list of shared objects before any
		# checking.  (done in another loop once the collection
		# is finished).
		#
		if ( is_lib($file) ) {
			my $b = basename($file);
			my $d = dirname($file);
			if ( $ld_path_all_libs ) {	# record lib dirname
				$Lib_Dirs{$d} = 1;
			}
			if ( $find_all_libs == 1 ) {	# record lib basename
				$lib_match{$b} = 1;
			}
		}
		push(@Check_List, $file);
	} else {
		check($file);
	}
}

#
# Wrapper for the dynamic and static checks.
#
sub check {
	my ($file) = @_;

	my (%dtneeded, $cnt);
	
	$cnt = 0;

	file_header($file);

	#
	# This SKIP is here instead of process() to let all libraries be
	# found (find_all_libs/ld_path_all_libs)
	#
	if (! $check_libraries && is_lib($file) ) {
		print "$file: SKIP: is a shared library\n";
		return;
	}

	$cnt += dynamic_check($file, \%dtneeded);
	$cnt += static_check($file);

	$problem_count += $cnt;	# increment by how many problems found.

	if ( ! $cnt ) {
		print "$file: OK\n";	# no problems found, indicate OK.
	}
}

#
# optionally put extra tags and info at top for automatic parsers (-V).
#
sub file_header {
	my($file) = @_;
	
	if ($insane_verbosity) {
		print "BEGIN FILE: $file\n";
		print "BEGIN LD_LIBRARY_PATH: $ENV{LD_LIBRARY_PATH}\n";
		print "BEGIN ld_path: $ld_path\n";
		print "BEGIN LD_PRELOAD: $ENV{LD_PRELOAD}\n";
	}
}

#
# Extracts the command line options via getopts()
# if -f switch has been specified, reads the filelist and append filenames
# to @ARGV for processing.
#
sub get_options {
	#
	# We want the -l option to be additive.  For simplicity we gather
	# all of them them up now, and just ignore $opt{l} later:
	#
	my $lib_str = '';
	my $lib_rgx = '';
	my $i;
	my @orig = @ARGV;
	for ($i=0; $i < @ARGV; $i++) {
		if ( $ARGV[$i] eq '-l' ) {
			my $str = $ARGV[$i+1];
			if ( $str =~ m,^/(.*)/$, ) {
				# regex: /libfoo.*/
				$lib_rgx .= $1 . '|';
			} else {
				# fixed string
				$lib_str .= $str . '|';
			}
		}
	}
	$lib_str =~ s/\|+$//;		# trim any trailing |
	$lib_rgx =~ s/\|+$//;
	if ( $lib_rgx ne '' ) {
		$lib_match = $lib_rgx;
	}
	if ( $lib_str ne '' ) {
		my $lib;
		foreach $lib (split(/\|/, $lib_str)) {
			$lib = basename($lib);
			$lib_match{$lib} = 1;
		}
	}

	# now call ordinary getopts:
	my %opt;
	if ( ! getopts('kh?aIvVf:o:p:e:l:L:j:d:D:s:S:O:A:', \%opt) ) {
		usage();
		exit 1;
	}

	# help:
	if ( exists($opt{h}) || exists($opt{'?'}) ) {
		help();
		exit 1;
	}

	# boolean switches:
	$keep_going = 1 if $opt{k};

	$verbose = 1 if $opt{v};
	$insane_verbosity = 1 if $opt{V};

	$find_all_libs = 1 if $opt{a} && ! $ENV{AC_PARALLEL};
	$check_libraries = 0 if $opt{I};

	# options and environment for parallel jobs
	if (exists $opt{j} && $opt{j} ne '') {
		$njobs = $opt{j};
	} elsif (exists $ENV{AC_NJOBS} && $ENV{AC_NJOBS} ne '') {
		$njobs = $ENV{AC_NJOBS};
	}
	# child is a single job and so shuts it off:
	$njobs = 0 if $ENV{AC_PARALLEL};

	# there is also a trick to autodect number of cpus:
	if ( $njobs =~ /^(-|detect|n)$/i ) {
		my $ncpu = ncpu();
		$njobs = $ncpu;
		if ( $njobs == 2 ) {
			$njobs++;
		}
	}

	# user specified private and evolving matches: 
	$private_match  = $opt{p} if exists $opt{p} && $opt{p} ne '';
	$evolving_match = $opt{e} if exists $opt{e} && $opt{e} ne '';

	# output to $output_file instead of to STDOUT
	if (exists $opt{o} && $opt{o} ne '') {
		$output_file = $opt{o};
		if (! $ENV{AC_PARALLEL}) {	# only applies to master
			# redirect right now: 
			open(STDOUT, ">$output_file")
			    || die "cannot open: $output_file: $!\n";
		}
	}

	# user specified LD_LIBRARY_PATH
	if ( exists $opt{L} ) {
		$ld_path = $opt{L};
		my $path;
		foreach $path (split(/:/, $ld_path)) {
			# need to find all libs for special token "find" 
			$ld_path_all_libs = 1 if $path eq 'find';
		}
	}

	# various external database files:

	# public/private dynamic db:
	if ( exists $opt{D} ) {
		# -D means only use the db file.
		$private_db_file = $opt{D};
		$private_db_only = 1;
	} elsif ( exists $opt{d} ) {
		$private_db_file = $opt{d};
	}
	# public/private dynamic db overrides:
	if ( exists $opt{O} ) {
		$override_db_file = $opt{O};
	}
	# list of ABI libraries we are int:
	if ( exists $opt{A} ) {
		$abi_library_file = $opt{A};
	}
	# static linking db:
	if ( exists $opt{S} ) {
		# -S is static checking only.
		$static_archive_file = $opt{S};
		$static_only = 1;
	} elsif ( exists $opt{s} ) {
		$static_archive_file = $opt{s};
	}
	# this indicates skip the static checking:
	if ($static_archive_file =~ /^(skip|none)$/ ) {
		$static_archive_file = '';
		$static_check = 0;
		$static_only = 0;
	}

	# collect option args into @Rerun_Args for parallel mode:
	for ($i=0; $i < scalar(@orig) - scalar(@ARGV); $i++) {
		my $a = $orig[$i];
		# Try to clean up options for parallel mode:
		if ($njobs > 0) {
			if ( $a =~ /^-[jfLlo]$/ ) {
				$i++;
				next;
			} elsif ( $a =~ /^-[jfLlo]./ ) {
				next;
			} elsif ( $a =~ /^-[ka]$/ ) {
				next;
			}
		}
		push(@Rerun_Args, $a);
	}

	#
	# read in the -f list of binaries to check and append
	# them to @ARGV
	#
	if ( defined $opt{f} ) {
		if ( $opt{f} ne '-' && ! -f $opt{f} ) {
			# '-' is STDIN, so the list can be piped in.
			die "invalid -f option: $opt{f}: $!\n";
		}

		open(LIST, "<$opt{f}") || die "cannot open: $opt{f}: $!\n";
		while (<LIST>) {
			chomp;
			push(@ARGV, $_);
		}
		close(LIST);
	}
}

#
# Determine OS, exit if not supported, adjust PATH, etc.
#
sub os_setup {

	# This path will find uname(1) and other needed utils:
	$ENV{PATH} .= ":/bin:/usr/bin";

	$ENV{LC_ALL} = "C";

	my ($os, $rel);
	chomp($os  = `uname -s`);
	chomp($rel = `uname -r`);

	if ( $os =~ /^SunOS/ && $rel !~ /^4\./ ) {
		# Note we cannot do SunOS 4.x (original SunOS & a.out)
		$OS = 'solaris';
		$ENV{PATH} .= ":/usr/ccs/bin";	# for dump(1)
		system("type elfdump >/dev/null 2>&1");
		if ($? == 0) {
			$Cmds{elfdump} = 1;
		} else {
			$Cmds{elfdump} = 0;
		}

	} elsif ( $os =~ /linux/i ) {
		#
		# We call this case "linux", though there are many
		# distros and version levels, some for which this tool
		# may not work.  Our actual requirement is the GNU
		# linker version 2.1 or higher.
		#
		$OS = 'linux';

	} else {
		die "unsupported OS: $os $rel\n";
	}
}

#
# Check a binary for any private or evolving symbol calls.
# Runs ldd -r with LD_DEBUG=files,bindings and parses the output,
# then checks the private/evolving database to see if the symbol is listed.
#
sub dynamic_check {
	my ($file, $dtneeded_ref) = @_;

	my $cnt = 0;

	# record inode for matching later on:
	my $file_inode = get_inode($file);

	#
	# quick test to see if it is completely statically linked:
	# this may give 2-5% performance hit due to fork.
	#
	my $file_output = `file '$file' 2>&1`;
	if ( $file_output =~ /statically linked/i ) {
		print "$file: STATIC_LINK: (completely statically linked)\n";
		return 1;
	}

	if ( $static_only ) {
		my $line;
		my $ldd_output = `ldd '$file' 2>&1`;
		foreach $line (split(/\n/, $ldd_output)) {
			if ( $line =~ /$dtneeded_match/o ) {
				$dtneeded_ref->{$1} = $2;
			}
		}
		return 0;
	}

	#
	# LD_DEBUG output may prepend "./" to objects. We remove it
	# from everything, including the filename we profile.
	#
	my $file0 = $file;	# store original name for output.
        $file =~ s,^\./,,;

	# open a pipe to a child process that will run ldd(1):
	my $pid = open(LDD, "-|");
	die "$!\n" if ! defined $pid;	# fork failed.

	if ( ! $pid ) {
		# child here, go run ldd -r with debug env:
		exec_ldd($file);
		exit 1;
	}
	
	my ($obj, $lib, $sym, $rest, $info, $tmp, %saw, %objhit, %libskip);
	my $bindings_cnt = 0;
	my $unbound_cnt  = 0;

	my $direct_only = 1;
	$direct_only = 0 if (%lib_match || $lib_match ne '');
	

	my $prefix = '';	# substrs for index() to speed line processing
	my $skip_prefix = '';

	# process the LD_DEBUG output:
	while (<LDD>) {
		chomp;

		if ( ! /$ld_debug_match/o ) {
			# regular ldd -r output line (no leading PID).

			if ( /$unbound_match/o ) {	# see set_matches()
				$sym = $1;
				$rest = $2;
				next if exists $saw{"UNBOUND|$sym"};
				$saw{"UNBOUND|$sym"} = 1;

				if (exists $Deleted{__DELETED__}{$sym}) {
					print "$file0: DELETED_SYM: $sym/unbound\n";
					$cnt++;
				}
				if ($insane_verbosity) {
					$rest =~ s/[()]//g;
					$rest = trim($rest);
					print "$file0: UNBOUND: ($rest) $sym\n";
				}

				$unbound_cnt++;

			} elsif ( /$dtneeded_match/o ) { # see set_matches()
				my $from = $1;
				my $to = $2;
				$dtneeded_ref->{$from} = $to;
				if (exists $Deleted{$from}{__ALL__}) {
					print "$file0: UNSTABLE_LIB: $from => $to\n";
					$cnt++;
				}
			}
			next;
		}

		if ( $prefix eq '' ) {
			#
			# this skips over the ldd bindings for other pids:
			#
			if($skip_prefix ne '' && index($_, $skip_prefix) == 0) {
				# n.b. substr() test gave no speedup.
				next;
			}

			#
			# This will detect if the "12345:" pid prefix applies
			# to the file we want checked.
			#
			if ( /$file_match/o ) {
				$tmp = $1;
				$obj = $2;
				my $ok = 0;
				if ( $obj eq $file ) {
					$ok = 1;
				} elsif ( defined $file_inode ) {
					my $obj_inode = get_inode($obj);
					if ( defined $obj_inode &&
					    $file_inode eq $obj_inode ) {
						$ok = 1;
					}
				}
				if ( $ok ) {
					$prefix = $tmp;
				} elsif ($lddstub_match ne '' &&
				    $obj =~ /$lddstub_match/o) {
					# processing library do not want to skip it.
					;
				} else {
					$skip_prefix = $tmp;
				}
			}
			next;
		} elsif ( index($_, $prefix) != 0 ) {
			# n.b. substr() test gave no speedup.
			next;
		}

		# leading PID: specific matches:

		if ( /$binding_match/o ) {	# see set_matches()
			$obj  = $1;
			$lib  = $2;
			$sym  = $3;
			$rest = $4;

			$obj =~ s,^\./,,;	# remove any leading ./
			$lib =~ s,^\./,,;

			my $ob = '';		# a string for output

			# ld_debug output for $file: 
			#    binding file=$obj to file=$lib symbol `$sym'

			if ($obj eq $lib || $lib eq $file) {
				# ignore self and reverse bindings
				next;

			} elsif (%libskip && exists $libskip{$lib}) {
				# quick skip for when ABI_Libs is used
				next;
			}

			if ($direct_only) {
				next if $obj ne $file;
				# note: filtering for direct only case is done.
			} elsif ($obj eq $file) {
				# this the primary one we want
				;
			} elsif (%objhit && exists $objhit{$obj}) {
				# avoid the lib_match lookup work below
				$ob = $objhit{$obj};
				next if $ob eq '';

			} else {
				#
				# $obj must be a shared lib here since we
				# already tested $obj eq $file above.
				#
				my $matched = 0;
				my $obj_b = basename($obj);

				if (%lib_match && exists $lib_match{$obj_b}) {
					$matched = 1;
				}
				if (! $matched && $lib_match ne ''
		 		    && $obj_b =~ /$lib_match/o) {
					$matched = 1;
				}
				if ($matched) {
					# set to desired output string:
					$ob = $obj_b . '->';
					$objhit{$obj} = $ob;
				} else {
					# set to null string to indicate skip
					$objhit{$obj} = '';
					next;
				}
			}

			if ( defined $Filter{$lib} ) {
				# apply the alias for the filter:
				$lib = $Filter{$lib};
				next if $obj eq $lib;
				next if $lib eq $file;
				next if %libskip && exists $libskip{$lib};
			}

			# skip repeated bindings (non-PIC):
			next if exists $saw{"$lib|$sym"};
			$saw{"$lib|$sym"} = 1;

			$bindings_cnt++;

			next if $Skipsym{$sym};

			#
			# set the inode for later use (we use the inodes of
			# libraries instead of pathnames to avoid chasing
			# down symlinks, etc)
			#
			my $lib_inode = get_inode($lib);

			if ( ! defined $lib_inode ) {
				die "invalid library: $lib, $!\n";
			}

			if (%ABI_Libs) {
				if (exists $ABI_Libs{$lib_inode} ||
				    exists $ABI_Libs{$lib}  ) {
					;	# let it through.
				} else {
					$libskip{$lib} = 1;
					next;
				}
				if (! $direct_only) {
					my $obj_inode = get_inode($obj);
					if (! defined $obj_inode) {
						$obj_inode = '__NONE__'; 
					}
					if (exists $ABI_Libs{$obj_inode} ||
					    exists $ABI_Libs{$obj}  ) {
						# skip intra ABI calls
						next;
					}
				}
			}

			load_classification($lib_inode, $lib, $sym, $rest);

			my $mentioned = 0;
			$info = $Private{$lib_inode}{$sym};
			if ( $info ) {
				$mentioned++;
				print "$file0: PRIVATE: ($ob$info) $sym\n";
			}
			$info = $Evolving{$lib_inode}{$sym};
			if ( $info ) {
				$mentioned++;
				print "$file0: EVOLVING: ($ob$info) $sym\n";
			}
			if ( %Deleted && exists $Deleted{__DELETED__}{$sym} ) {
				my $base = basename($lib);
				if ( exists $Deleted{$base}{$sym} ) {
					print "$file0: DELETED_SYM: $sym/$base\n";
					$mentioned++;
				}
			}
			$cnt += $mentioned;
			if ( $insane_verbosity && ! $mentioned ) {
				print "$file0: PUBLIC: ($ob$lib:public) $sym\n";
			}

		} elsif ( /$filter_match/o ) {	# see set_matches()
			my $filtee = $1;
			my $filter = $2;
			$filtee =~ s,^\./,,;
			$filter =~ s,^\./,,;

			if ( ! exists $Filter{$filtee} ) {
				load_filter($filtee, $filter);
			}
		}
	}
	#undef %saw;

	close(LDD);
	if ( $? != 0 ) {
		$cnt++;
		print "$file0: LDD_ERROR";
		print " (run ldd -r on binary for more info)\n";
		exit 1 unless $keep_going;

	} elsif ( $bindings_cnt == 0 ) {
		$cnt++;
		print "$file0: NO_BINDINGS";
		my $priv = '';
		$priv = 'SGID' if -g $file;
		$priv = 'SUID' if -u $file;
		if ( $priv ) {
			print " ($priv binary. ";
			print "no info from LD_DEBUG=bindings ldd -r)\n";
		} else {
			print " (run ldd -r on binary for more info)\n";
			
		}

		exit 1 unless $keep_going;
	}

	if ( $unbound_cnt != 0 ) {
		$cnt++;
		print "$file0: UNBOUND_SYMBOLS: $unbound_cnt";
		print " (run ldd -r on binary for more info)\n";
		exit 1 unless $keep_going;
	}

	return $cnt;
}

#
# Takes binding line information and determines if the lib:sym is private
# or evolving. Stores results in %Private and %Evolving databases.
#
sub load_classification {
	my ($inode, $lib, $sym, $rest) = @_;

	if ( $OS eq 'solaris' ) {
		# we are done if we have already processed this library:
		return if exists $Private{$inode};

		# set something so we only get in here once per lib:
		$Private{$inode}{'__LOADED_SYMS__'} = 1;

	} elsif ( $OS eq 'linux' ) {
		# return if we already processed the library and symbol: 
		return if exists $Private{$inode}{$sym};
	}
		
	my $cnt = 0;

	if ($private_db_only) {
		;	# skip to External_DB lookup below.

	} elsif ( $OS eq 'solaris' ) {
		
		return if $lib =~ /['\n]/;	# bad chars for shell
		if ( ! -e $lib ) {
			# the dynamic linker found it; so should we.
			die "invalid library: $lib: $!\n";
		}

		open(PVS, "pvs -dos '$lib' 2>/dev/null|") || die "$!\n";

		while (<PVS>) {
			chomp;
			my ($l, $d, $version, $sym) = split(' ', $_);
			$version =~ s/:$//;
			$sym =~ s/;$//;

			set_db($lib, $inode, $version, $sym);
			$cnt++;
		}
		close(PVS);

	} elsif ( $OS eq 'linux' ) {
		# load the classification one symbol at a time:

		my $version = '';
		if ( $rest =~ /^\s*\[(.*)\]/ ) {	# e.g. [GLIBC_2.1]
			$version = $1;
		}
		#
		# N.B.: the above will only work if the application being checked
		# has been was built against a *versioned* library. Otherwise
		# the dynamic linker does not provided the [VERS] info.
		#
		# To handle this properly, we will likely have to do
		# it the Solaris way with a method similar to pvs(1).
		#

		set_db($lib, $inode, $version, $sym);
		$cnt++ if $version ne '';
	}

	# if we did not find anything, see if an external db file is supplied:
	if ($cnt == 0 && ( exists $External_DB{$lib}
			|| exists $External_DB{$inode} ) ) {

		my $db = '';
		if ( exists $External_DB{$lib} ) {
			$db = $External_DB{$lib}; 
		} else {
			$db = $External_DB{$inode}; 
		}
		if ( $db !~ /\n/ ) {
			if (! open(EXT, "<$db")) {
				die "cannot open: $db: $!\n";
			}
			$db = '';
			while (<EXT>) {
				next if /^\s*#/;
				next if /^\s*$/;
				$db .= $_;
			}
			close(EXT);
		}
		my(@a, $line);

		foreach $line (split(/\n/, $db)) {
			my($version, $arch, $sym2);
			next if $line =~ /^\s*$/;
			@a = split(/\|/, $line);
			if ( @a == 1 ) {
				$version = 'private';
				$arch = 'none';
				$sym2 = $a[0];
			} elsif ( @a == 2 ) {
				$version = $a[0];
				$arch = 'none';
				$sym2 = $a[1];
			} elsif ( @a == 3 ) {
				$version = $a[0];
				$arch = $a[1];
				$sym2 = $a[2];
			}
			if ( $sym2 ne '' ) {
				set_db($lib, $inode, $version, $sym2);
			}
		}

		#
		# XXX there is an issue here if some other value of $lib
		# will be found corresponding to this inode... (for both
		# solaris and linux)
		#
		if ($OS eq 'linux' && ! exists $Private{$inode}{$sym} ) {
			# need to set a marker to not return.
			$Private{$inode}{$sym}  = undef;
		}
	}
}
	
#
# Set the entries in the %Private and %Evolving database hashes.
# Both are referenced by the inode of the library, then the symbol name.
# The value is a little piece of information to report to user when usage
# is detected.
#
# $base is only used to look up Overrides and to go in the info piece.
#
sub set_db {
	my ($lib, $inode, $version, $sym) = @_;

	$Private{$inode}{$sym}  = undef;
	$Evolving{$inode}{$sym} = undef;

	my $have_override = 0;
	my $base = basename($lib);
	if ( exists $Override{$base} ) {
		$have_override = 1;
	}

	my $lname = $base;
	$lname = $lib if $verbose;

	if( $have_override && ! $extended_private_match &&
	    ! $extended_evolving_match && exists $Override{$base}{$sym} ) {

		return if $Override{$base}{$sym} eq 'public';
	}

	if( $extended_private_match ) {

		if ( $version ne '' && 
		    "${version}::${sym}" =~ /$private_match/o ) {

			$Private{$inode}{$sym} = "$lname:$version";
			return;
				
		} elsif ( "${base}::${sym}" =~ /$private_match/o ) {
			$Private{$inode}{$sym} = "$lname";
			return;
		}

	} elsif ( $have_override && exists $Override{$base}{$sym}
	    && $Override{$base}{$sym} eq 'private' ) {

		$Private{$inode}{$sym} = "$lname:private";
		return;

	} elsif ( $version ne '' && $version =~ /$private_match/o ) {

		$Private{$inode}{$sym} = "$lname:$version";
		return;
	} 

	#
	# Simple additional heuristic for GLIBC (leading underscore)
	# see special_case_syms().
	#
	if( $OS eq 'linux' && ! $extended_private_match &&
	    ! defined $Override{$base}{$sym} && $version =~ /^GLIBC_/ ) {

		if ( $sym =~ /^_/ && 'private' =~ /$private_match/o ) {
			$Private{$inode}{$sym} = "$lname:$version";
			return;
		}
	}
			
	if( $extended_evolving_match ) {

		if ( $version ne '' && 
		    "${version}::${sym}" =~ /$evolving_match/o ) {

			$Evolving{$inode}{$sym} = "$lname:$version";
			return;
				
		} elsif ( "${base}::${sym}" =~ /$evolving_match/o ) {
			$Evolving{$inode}{$sym} = "$lname";
			return;
		}

	} elsif ( $have_override && exists $Override{$base}{$sym}
	    && $Override{$base}{$sym} eq 'evolving' ) {

		$Evolving{$inode}{$sym} = "$lname:evolving";
		return;

	} elsif ( $version ne '' && $version =~ /$evolving_match/o ) {

		$Evolving{$inode}{$sym} = "$lname:$version";
		return;
	}
}

#
# Record filter library "aliasing" for auxiliary filter libraries.
# The global hash %Filter will be used to apply the aliasing.
#
sub load_filter {
	my ($filtee, $filter) = @_;
	
	# set it to undef to indicate we have touched this one:
	$Filter{$filtee} = undef;

	if ( ! -e $filter ) {
		# the dynamic linker found it; so should we.
		die "invalid filter library: $filter: $!\n";
	}

	#
	# These characters would never happen under normal circumstances,
	# but could cause our shell commands to fail:
	#
	return if $filter =~ /['\n]/;

	# currently only know how to handle filters on Solaris:

	if ( $OS eq 'solaris' ) {
		my $dump = `dump -Lv '$filter' 2>/dev/null`;
		my $base = basename($filtee);
		if ($dump =~ /\bAUXILIARY\b.*\b${base}\b/ ||
		    $filter =~ /\blibdl\.so\.\d+/) {
			$Filter{$filtee} = $filter;	# set the alias.
		}
	} elsif ( $OS eq 'linux' ) {
		return;
	}
}

#
# Load any external symbol databases.
#
sub load_external_dbs {
	if ($static_archive_file ne '' && $static_archive_file ne 'int') {
		my $db;
		foreach $db (split(/,/, $static_archive_file)) {
			if ( ! -e $db ) {
				# errant comma in path?
				load_static_archive_db($static_archive_file);
				last;
			}
			load_static_archive_db($db);
		}
	}
	if ($private_db_file) {
		load_private_db_file($private_db_file);
	}
	if ($abi_library_file) {
		load_abi_library_file($abi_library_file);
	}
}

sub load_static_archive_db {
	my($db) = @_;
	if ( ! -e $db ) {
		die "invalid static archive database: $db, $!\n";
	}

	my $filetype = `file '$db'`;
	my $is_archive = 0;
	if ( $OS eq 'solaris' || $OS eq 'linux' ) {
		if ( $filetype =~ /ar\s+archive/ ) {
			$is_archive = 1;
		}
	}
	if ($is_archive) {
		load_archive($db);
	} else {
		open(DB, "<$db") || die "cannot open: $db: $!\n";
		while (<DB>) {
			chomp;
			next if /^\s*#/;
			next if /^\s*$/;
			# format: read_vtoc|/usr/lib/libadm.a:rdwr_vtoc.o
			my ($sym, $rest) = split(/\|/, $_, 2);
			my ($lib, $file) = split(/:/, $rest, 2);
			add_archive($sym, $lib, $file);
		}
	}
	#
	# In the case of there being no archives on the current machine,
	# the AR_* hashes will not be setup, leading to uninitilized use (-w)
	# during the checking.  So we set some values here.
	#
	$AR_sym{__NO_SYMBOL__} = 1 if ! %AR_sym;
	$AR_cnt{__NO_SYMBOL__} = 1 if ! %AR_cnt;
	$AR_lib{__NO_SYMBOL__} = 1 if ! %AR_lib;
}

# add a sym, lib, module triple to the static archive database
sub add_archive {
	my($sym, $lib, $file) = @_;
	$AR_sym{$sym}{"$file:$lib"} = 1; 
	$AR_cnt{"$file:$lib"}++;
	$AR_lib{$lib}++;
}

#
# Examine a library archive and record the static linking lookup
# information.
#
sub load_archive {
	my($lib) = @_;

	return if exists $AR_lib{$lib}; # do not repeat an archive.

	#
	# These archive object files are problematic in that their
	# symbols are too common that they cause many false positives.
	#
	my (%skip, $skip);
	foreach $skip (@Archive_Skip) {
		$skip{$skip} = 1;
	}
	
	if ( $OS eq 'solaris' ) {
		
		# Run elfdump to extract the archive file members and symbols:

		if (! $Cmds{elfdump}) {
			die "no elfdump(1), cannot process archive $lib";
		}

		open(ELFDUMP, "elfdump -s -N ARSYM '$lib'|")
		    || die "elfdump -s -N ARSYM '$lib': $!\n";

		$AR_lib{$lib} = 0;
		while(<ELFDUMP>) {
			#
			# e.g.: [1]  0x000022ac  (_addchnstr.o):addchnstr
			#                         (    $1     ) (  $2   )
			#
			if ( /^\s*\[.*\((.*)\):(\S+)/ ) {
				my $file = $1;
				my $sym = $2;

				next if $skip{$file};
				add_archive($sym, $lib, $file);
			}
		}
		close(ELFDUMP);

	} elsif ( $OS eq 'linux' ) {

		open(OBJDUMP, "objdump -t '$lib' 2>/dev/null|")
		    || die "objdump -t '$lib': $!\n";

		my $current_module = '';

		$AR_lib{$lib} = 0;
		while(<OBJDUMP>) {
			#
			# e.g.: nis_clone_obj.o:     file format elf32-i386
			#
			if ( /^(\S+):\s+file/ ) {
				$current_module = $1;
				next;
			}
			#
			# 00000450 g     F .text  00000309 __nis_create_callback
			#                         (  $1  ) (  $2               )
			#
			if ( /\s[gG]\s+\S*\s+\.text\b.*\s(\S+)$/ ) {
				my $file = $current_module;
				my $sym = $1;

				if ( $file eq '' ) {
					die "could not find module "
					    . "file name for archive $lib\n";
				}
				
				next if $skip{$file};
				add_archive($sym, $lib, $file);
			}
		}
		close(OBJDUMP);
	}
}

#
# Read in an externally supplied public/private database file.
#
sub load_private_db_file {
	my($db) = @_;

	if ( ! -e $db ) {
		die "invalid private archive database: $db, $!\n";
	}
	open(DB, "<$db") || die "cannot open: $db: $!\n";
	while (<DB>) {
		chomp;
		next if /^\s*#/;
		next if /^\s*$/;

		my ($lib, $rest) = split(/\|/, $_, 2);
		if ($rest =~ /^FILE=(.*)/) {
			# keep a reference to a file to read in on demand:
			my $file = $1;
			$External_DB{$lib} = $file;
		} else {
			# otherwise store the text for this lib:
			$External_DB{$lib} .= $rest . "\n";
		}
	}
	close(DB);

	# copy over filename paths to inode lookup:
	my ($lib, $inode);
	foreach $lib (keys(%External_DB)) {
		next if ! -e $lib;
		$inode = get_inode($lib);
		if ( defined $inode && ! exists $External_DB{$inode} ) {
			$External_DB{$inode} = $External_DB{$lib};
		}
	}
}

#
# read in a list of ABI libraries to check calls into:
# (calls into all other libraries will be ignored)
#
sub load_abi_library_file {
	my ($lib_file) = @_;

	open(ABI_LIBS, "<$lib_file") || die "cannot open: $lib_file: $!\n";
	while (<ABI_LIBS>) {
		chomp;
		next if /^\s*#/;
		next if /^\s*$/;

		my $lib = $_;
		$ABI_Libs{$lib} = 1;

		my $inode = get_inode($lib);

		if ( defined $inode ) {
			$ABI_Libs{$inode} = 1;
		}
	}
	close(ABI_LIBS);
}

#
# exec ldd -r (or equivalent) on binary. Never returns, parent
# will read command output.
#
sub exec_ldd {
	my ($file) = @_;

	open(STDERR, ">&STDOUT");
	# need to close stdin on linux for some suid programs e.g. chsh (!)
	close(STDIN);

	if ( defined $ld_path ) {
		$ENV{LD_LIBRARY_PATH} = $ld_path;
	}

	# currently, no difference between OSs
	$ENV{LD_DEBUG} = "files,bindings";
	exec 'ldd', '-r', $file;
	exit 1;	# exec failed
}

#
# Adjust $ld_path under $ld_path_all_libs by substituting 'find'
#
sub modify_ld_path {
	my $new = '';
	my $path;
	foreach $path (keys(%Lib_Dirs)) {
		next if $path =~ /^\s*$/;
		$new .= $path . ":";
	}
	$new =~ s/:+$//;

	my $new_ld_path = '';
	foreach $path (split(/:/, $ld_path)) {
		next if $path =~ /^\s*$/;
		if ( $path eq 'find' ) {
			if ( $new ne '' ) {
				$new_ld_path .= $new . ":";
			}
		} else {
			$new_ld_path .= $path . ":";
		}
	}
	$new_ld_path =~ s/:+$//;
	$ld_path = $new_ld_path;
}

#
# Perform the static linking of system library archives check.
# The heuristic is simply to see if "well-known" symbols exist in the
# binary's .text area.
#
sub static_check {
	my ($file) = @_;

	return 0 if ! $static_check;

	my (%syms, %linkslib, $lib);

	#
	# N.B. technically we should use the %linkslib set below to verify
	# that the binary does not actually have the library on its DTNEEDED
	# list. Currently need to check if the above $dtneeded_ref does
	# not include extra libs from the full closure of the dynamic linking.
	#
	# XXX need to have $dtneeded_ref passed in if we are doing this,
	# that causes a slowdown under -S.  see the calling code where
	# dynamic_check is currently skipped under -S.
	#
#	my $dtlib;
#	foreach $dtlib ( keys %{$dtneeded_ref} ) {
#		$linkslib{libc} = 1      if $dtlib =~ m,\blibc\.so,;
#		$linkslib{libsocket} = 1 if $dtlib =~ m,\blibsocket\.so,;
#		$linkslib{libnsl} = 1    if $dtlib =~ m,\blibnsl\.so,;
#	}

	get_syms($file, \%syms);

	my $cnt = 0;

	if ( $static_archive_file && %AR_sym ) {
		# use external list:
		$cnt += static_check_from_database($file, \%syms);
		
	} elsif ( $OS eq 'solaris' ) {
		
		# libc.a:
		if ($syms{'_exit'}) {
			$cnt++;
			print "$file: STATIC_LINK: libc.a\n";
		}

		# libsocket.a:
		if ($syms{'socket'} && $syms{'_socket'} &&
		    $syms{'bind'} && $syms{'_bind'} &&
		    $syms{'connect'} && $syms{'_connect'}) {
			print "$file: STATIC_LINK: libsocket.a\n";
			$cnt++;
		}

		# libnsl.a:
		if ($syms{'_xti_bind'} && $syms{'_xti_connect'} &&
		    $syms{'_tx_bind'} && $syms{'_tx_connect'}) {
			print "$file: STATIC_LINK: libnsl.a\n";
			$cnt++;
		}
	} elsif ( $OS eq 'linux' ) {
		
		# libc.a:
		if ($syms{'_exit'}) {
			$cnt++;
			print "$file: STATIC_LINK: libc.a\n";
		}
	}
	return $cnt;
}

#
# Check (somewhat heuristically) for any static linking of static
# archives in the database.
#
sub static_check_from_database {
	my($file, $sym_ref) = @_;

	my(%hits, %hitsyms);

	my $cnt = 0;
	my $sym;
	foreach $sym (keys( %{$sym_ref} ) ) {
		if ( exists $AR_sym{$sym} ) {
			my $id;
			foreach $id ( keys %{$AR_sym{$sym}} ) {
				#
				# collect hits. $id is an object file name
				# plus pathname tag "file.o:lib"
				#
				$hits{$id}++;
				push(@{$hitsyms{$id}}, $sym);
			}
		}
	}

	if ( ! %hits ) {
		# there can be no problems if there were no hits.
		return 0;
	}

	my (%static_links, %lib_hits, %long_msg, %short_msg);

	#
	# Now look for specific overlaps with the object file buckets,
	# using these fudge factors:
	#
	# minimum_syms_per_module: e.g. 4 means 4 symbols minimum per module.o
	# 0 indicates the constraint is not applied.
	#
	my $minimum_syms_per_module = 0;
	#
	# minimum_fraction: e.g. 0.9 means 90% or more of module.o must be hit.
	#
	my $minimum_fraction = 1.00;
	#
	# minimum_syms_per_library: e.g. 3 means we must have 3 or more
	# hits over the whole library before we give the warning.
	#
	my $minimum_syms_per_library = 3;

	# Loop over the id's (i.e the "file.o:lib" tags) 
	my $id;
	foreach $id (keys %hits) {

		# Check if the module, i.e. file.o, is too small:
		my $size = $AR_cnt{$id};
		next if $size < $minimum_syms_per_module;

		# Next, check if we have an acceptable fraction of them:
		my $f = $minimum_fraction * $size;
		my $n = int($f);
		$n++ if $n < $f;

		next if $hits{$id} < $n;

		# If so, set up the various warning variables:

		my($module, $lib) = split(/:/, $id, 2);
		$static_links{$lib} = 1;
		$lib_hits{$lib} += $hits{$id};
		$long_msg{$lib} .= "\t$module\[$hits{$id}/$size\] "
		    . join( ', ', @{$hitsyms{$id}} ) . "\n";
		$short_msg{$lib} .= "$module\[$hits{$id}/$size\],"
	}

	if ( ! %static_links ) {
		# No modules overlapped enough, so no problems detected:
		return 0;
	}

	# Print out the warning messages:
	my ($lib, %didlib);

	foreach $lib (keys %static_links) {
		my $msg;
		next if $lib_hits{$lib} < $minimum_syms_per_library;
		my $blib = $lib;
		if ( $lib =~ /^[\d.]+--(.*)$/ ) {
			#
			# handle the full release N.M--/usr/lib/...
			# notation, and print only one
			#
			$blib = $1;
			next if $didlib{$blib};
			$didlib{$blib} = 1;
		}
		my $msg_l = "\n" . $long_msg{$lib};
		my $msg_s = $short_msg{$lib};
		$msg_s =~ s/,+$//;
		$msg = "($lib_hits{$lib} symbols overlap)";
		$msg .= $msg_l if $verbose;
		print "$file: STATIC_LINK: $blib $msg\n";
		$cnt++;
	}
	return $cnt;
}

#
# Extract the symbols defined in the binary's .text section.
# Even if the the binary is stripped, these will be in .dynsym because
# during dynamic linking shared objects may bind to these symbols.
#
sub get_syms {
	my ($file, $sym_ref) = @_;

	#
	# run the respective binary utilities and extract the .text symbols
	# TODO: extend to .bss, .data, etc.
	#

	if ( $OS eq 'solaris' ) {

		if ($Cmds{elfdump}) {
			open(ELFDUMP, "elfdump -s -N .dynsym '$file' 2>/dev/null |")
			    || die "$!\n";

			# line looks like:
			# [88]  0x00011110 0x00000974  FUNC GLOB 0   .text       main
			while (<ELFDUMP>) {
				chomp;
				if (/\s\.text\s+(\S+)$/) {
					$sym_ref->{$1} = 1;
				}
			}
			close(ELFDUMP);
		} else {
			open(NM, "nm -Dp '$file' 2>/dev/null |") || die "$!\n";

			# line looks like:
			# 0000311596 T Load_input_line
			while (<NM>) {
				chomp;
				if (/^\w+\s+T\s+(\S+)$/) {
					$sym_ref->{$1} = 1;
				}
			}
			close(NM);
		}

	} elsif ( $OS eq 'linux' ) {
		# previously had -T ...
		open(OBJDUMP, "objdump -t '$file' 2>/dev/null |") ||
		    die "$!\n";
		
		# line looks like:
		# 08051660 g    DF .text  0000001a  Base        _exit
		while (<OBJDUMP>) {
			chomp;
			if ( /\s[gG]\s+\S*\s+\.text\b.*\s(\S+)$/ ) {
				$sym_ref->{$1} = 1;
			}
		}
		close(OBJDUMP);
	}
}

#
# call the various initialization subroutines:
#
sub data_setup {
	set_matches();
	set_extended_flags();
	special_case_syms();
}

sub set_extended_flags {
	#
	# Store whether we are doing extended matching for private or
	# evolving checks. These are set by -p & -e with a component of
	# the regex looking like:
	#
	#   -p 'GLIBC.*::__str'      (set syms __str* to private in GLIBC pkg)
	#   -p 'libgkt.*::__gtk_'    (set syms __gtk_* to private in libgtk)
	#
	# (those are made-up examples)
	#
	if ( $private_match =~ /::/ ) {
		$extended_private_match = 1;
	}
	if ( $evolving_match =~ /::/ ) {
		$extended_evolving_match = 1;
	}
}

#
# sets and compiles the regex's for parsing the LD_DEBUG output.
#
sub set_matches {
	#
	# Set the various pattern matches for parsing the LD_DEBUG output.
	# They are similar for the OS's, but not the same. If that
	# output ever changes this script will no longer work...
	#
	# One can LD_DEBUG=files,bindings ldd -r <binary> to see what the 
	# output looks like on a given OS.
	# 
	if ( $OS eq 'solaris' ) {

		$ld_debug_match = q/^\d/;
		$lddstub_match = q/\blddstub\b/;
		$binding_match =
		    q/binding file=(.*) to file=(.*): symbol `(\w+)'(.*)$/; 
		$filter_match  =
		    q/file=(.*);  filtered by (.*)$/;
		$unbound_match =
		    q/^\s*symbol not found:\s+(\S+)(.*)/;
		$dtneeded_match =
		    q/^\s*(\S+)\s+=>\s+(\(file not found\)|\S+)/;
		$file_match =
		    q/^(\d+:)\s+file=(.*);\s+analyzing/;

	} elsif ( $OS eq 'linux' ) {

		#
		# be careful, is it "normal symbol" in newer GLIBC, 
		# just "symbol" in earlier GLIBC.
		#
		$ld_debug_match = q/^\s*\d+:/;
		$lddstub_match = '';
		$binding_match =
		    q/binding file (.*) to (.*): .*symbol `(\w+)'(.*)$/;
		$filter_match  =
		    q/file=(.*);  filtered by (.*)$/; # XXX not checked
		$unbound_match =
		    q/^\s*undefined symbol:\s+(\S+)(.*)/;
		$dtneeded_match =
		    q/^\s*(\S+)\s+=>\s+(not found|\S+)/;
		$file_match =
		    q/^(\s*\d+:).*needed by (.*)$/;
		#
		# n.b. there is a PID mismatch in the GNU linker output if
		# we watch for the more sensible:
		#   q/^(\d+:)\s+file=(.*);\s+generating link map/;
		#
	}
}

sub special_case_syms {

	my (@public, @private, @evolving, @skip, @archive_skip);
	my ($sym, $pair);

	get_special_case_syms(\@public, \@private, \@evolving, \@skip,
	    \@archive_skip);

	if ($private_db_only) {
		# user supplied public/private database and wants only that.
		@public = ();
		@private = ();
		@evolving = ();
	}
	
	# Symbols to ignore:
	foreach $sym (@skip) {
		$Skipsym{$sym} = 1;
	}

	# Noisy archives to skip:
	@Archive_Skip = @archive_skip;

	# Symbols to reset to public:
	foreach $pair (@public) {
		my ($lib, $sym) = split(/:/, $pair);
		$Override{$lib}{$sym} = 'public';

		$lib =~ s/\.so.*$/.so/;
		$Override{$lib}{$sym} = 'public';
	}

	# Symbols to reset to evolving:
	foreach $pair (@evolving) {
		my ($lib, $sym) = split(/:/, $pair);
		$Override{$lib}{$sym} = 'evolving';

		$lib =~ s/\.so.*$/.so/;
		$Override{$lib}{$sym} = 'evolving';
	}

	# Symbols to reset to private:
	foreach $pair (@private) {
		my ($lib, $sym) = split(/:/, $pair);
		$Override{$lib}{$sym} = 'private';

		$lib =~ s/\.so.*$/.so/;
		$Override{$lib}{$sym} = 'private';
	}

	# read in data from use supplied override file:
	if ($override_db_file) {
		open(OVERRIDE, "<$override_db_file")
		    || die "cannot open: $override_db_file: $!\n";
		while (<OVERRIDE>) {
			chomp;
			next if /^\s*#/;
			next if /^\s*$/;
			my($lib, $sym, $class) = split(/\|/, $_, 3);

			# XXX what to do about full path for overrided?
			###$Override{$lib}{$sym} = $class;

			if ($class eq 'deleted') {
				$Deleted{$lib}{$sym} = 1;
				if ($sym ne '__ALL__') {
					$Deleted{__DELETED__}{$sym} = 1;
				}
				next;
			}
			if ($lib eq '__SKIP__') {
				$Skipsym{$sym} = 1;
				next;
			}

			$lib = basename($lib);
			$Override{$lib}{$sym} = $class;

			$lib =~ s/\.so.*$/.so/;
			$Override{$lib}{$sym} = $class;
		}
		close(OVERRIDE);
	}
}

sub trim {
	my ($x) = @_;
	$x =~ s/^\s*//;
	$x =~ s/\s*$//;

	return $x;
}

#
# Determine inode of file. Returns undef if there was a problem.
# (including if it could not read the file: stat failed).
#
sub get_inode {
	my ($file) = @_;

	if ( exists $Inodes{$file} ) {
		return $Inodes{$file};
	}
	my ($dev, $inode) = (stat($file))[0,1];
	if (defined $inode) {
		$inode = "$inode:$dev";
	}
	$Inodes{$file} = $inode;

	return $inode;
}

#
# Determine if a file is ELF, returns 1 if so, 0 otherwise
# (including if it could not read the file)
#
sub is_elf {
	my ($file) = @_;
	open(FILE, "<$file") || return 0;

	my ($n, $buf);
	$n = read(FILE, $buf, 4);
	close(FILE);

	if ( $n != 4 || $buf ne "\177ELF" ) {
		return 0;
	}
	return 1;
}

#
# Use a simple criterion to see if a file is a library
# shared object.
#
sub is_lib {
	my ($file) = @_;

	my $base = basename($file);
	if ( $base =~ /^lib.*\.so/ ) {
		# just a naming convention, but good enough
		return 1;
	}

	# less accurate, skip for now
	#} elsif ( $base =~ /\.so[\d.]*$/ ) {

	# otherwise try the output of file(1):
	if ( $OS eq 'solaris' ) {
		my $file_output = `file '$file'`;
		$file_output =~ s/^.*?:\s//;
		if ( $file_output =~ /dynamic lib/ ) {
			return 1;
		}
	} elsif ( $OS eq 'linux' ) {
		my $file_output = `file - < '$file'`; # for symlinks
		$file_output =~ s/^.*?:\s//;
		if ( $file_output =~ /shared object/ ) {
			return 1;
		}
	}

	return 0;
}

#
# Run $njobs jobs in parallel (e.g. on SMP machine).
#
sub parallel {
	my (%tmps, $job, $p, $file);

	my @args = @Rerun_Args;

	if ($find_all_libs || $ld_path_all_libs) {
		my $serial = 0;
		if ($serial) {
			#
			# unfortunately, we do this part serially to
			# obtain @Check_List before proceeding:
			#
			foreach $file (@ARGV) {
				#
				# Does not actually check $file except
				# for SKIPs.  Builds @Check_List and
				# lib info instead.
				#
				process($file);
			}

		} else {
			# experiment to do the lib search in parallel too:

			wait;	# needed to clear out a previous child(??)

			@Check_List = @ARGV;

			# run case 1 in parallel:
			my ($err, $prob) = do_parallel('lib_find');
			if ($err) {
				die "error in parallel task lib_find\n";
			}
		}

		if ($ld_path_all_libs) {
			modify_ld_path();
			if ( $ld_path ne '' ) {
				push(@args, '-L', $ld_path);
			}
		}
		if ( $lib_match ne '' ) {
			push(@args, '-l', "/$lib_match/");
		}
		if ( %lib_match ) {
			my $str = join('|', keys(%lib_match));
			push(@args, '-l', $str);
		}
	} else {
		@Check_List = @ARGV;
	}

	wait;	# needed to clear out a previous child(??)

	$ENV{AC_PARALLEL} = 1;

	# run case 0 in parallel:
	my ($error_count, $problem_count) = do_parallel('check', @args);

	if ( $error_count ) {
		exit 1;
	} elsif ( $problem_count ) {
		exit 2;
	} else {
		exit 0;
	}
}

#
# Manages the (current) two types of tasks we want to parallize:
#	'check')	The actual checking.
#	'lib_find')	Find libraries (i.e. for -a  and/or -L find)
#
sub do_parallel {

	my ($case, @args) = @_;		# @args is for case 'check' ...

	my (%tmps, $job, $p, $file);

	# spawn the child processes for the task:

	for ($job=0; $job < $njobs; $job++) {
		my $tmpfile0 = "/tmp/abicheck.in.$$.$job";
		my $tmpfile1 = "/tmp/abicheck.out.$$.$job";
		push(@tmpfiles, $tmpfile0, $tmpfile1);

		if ($case eq 'check') {
			# load the files to be processed by this job:
			open(TMP, ">$tmpfile0") || (killpids(), exit 1);
			my $cnt = 0;
			foreach $file (@Check_List) {
				if ($cnt++ % $njobs == $job) {
					print TMP $file, "\n";
				}
			}
			close(TMP);
		}

		$p = fork();
		if (! defined $p) {
			my $err = $!;
			killpids();
			die "could not fork: $err\n";
		} elsif ($p) {
			# parent:
			push(@pids, $p);
			$tmps{$p} = $tmpfile1;
		} else {
			# child:
			close(STDOUT);
			open(STDOUT, ">$tmpfile1");

			if ($case eq 'check') {
				push(@args, '-k', '-f', $tmpfile0);

				print STDERR "job $job ($$) running: ",
				    basename($0);
				if (! $ENV{AC_QUIET}) {
					print STDERR " ", join(' ', @args);
				}
				print STDERR "\n";
				exec $0, @args;
				exit 1;	# child must exit
				
			} elsif ($case eq 'lib_find') {
				my $cnt = 0;
				my $libs = 0;
				my $try = 0;
				foreach $file (@Check_List) {
				    if ($cnt++ % $njobs == $job) {
					$try++;
					if (is_elf($file) && is_lib($file)) {
						$libs++;
						print "LIB: $file\n";
					}
				    }
				}
				if (! $ENV{AC_QUIET}) {
					print STDERR "job $job ($$) "
					    . "LIB FOUND($libs/$try/$cnt)\n";
				}
				exit 0;	# child must exit
			}
		}
	}

	$SIG{INT}  = \&interrupted;
	$SIG{TERM} = \&interrupted;

	my $error_count = 0;
	my $problem_count = 0;

	# wait for child processes to finish:

	for ($job=0; $job < $njobs; $job++) {
		$p = wait;
		my $rc = $?/256;
		if ($tmps{$p} && open(TMP, "<$tmps{$p}")) {
			while (<TMP>) {
				if ($case eq 'check') {
					print;
				} elsif ($case eq 'lib_find') {
					chomp;
					if ( /^LIB: (.*)$/ ) {
						my $file = $1;
						my $b = basename($file);
						my $d = dirname($file);
						if ( $ld_path_all_libs ) {
							$Lib_Dirs{$d} = 1;
						}
						if ( $find_all_libs == 1 ) {
							$lib_match{$b} = 1;
						}
					}
				}
			}
			close(TMP);
			unlink($tmps{$p});
			delete($tmps{$p});
		} else {
			$error_count++;
		}
		if ($rc == 0) {
			;
		} elsif ($rc == 2) {
			$problem_count++;
		} else {
			$error_count++;
		}
	}

	$SIG{INT}  = 'DEFAULT';
	$SIG{TERM} = 'DEFAULT';

	unlink(@tmpfiles);

	@pids = ();
	@tmpfiles = ();

	return ($error_count, $problem_count);
}

#
# Emergency killing of child processes, tmpfiles removal, etc.
#
sub interrupted {
	killpids();
	exit 1;
}

sub killpids {
	kill 'INT',  @pids;
	kill 'TERM', @pids;
	kill 'KILL', @pids;
	unlink(@tmpfiles);
}

#
# used with njobs "-j detect" or "-j N",  switch, try to determine the
# number of cpus.
#
sub ncpu {
	my $output = '';
	if ($OS eq 'solaris') {
		my $psrinfo = "/usr/sbin/psrinfo";
		$psrinfo = "psrinfo" if ! -x $psrinfo;
		$output = `$psrinfo 2>/dev/null`;
	} elsif ( $OS eq 'linux' ) {
		$output = `egrep '^processor[ 	]*:' /proc/cpuinfo 2>/dev/null`;
	}

	my $ncpu = 0;
	my $line;
	foreach $line (split(/\n/, $output)) {
		next if $line =~ /^\s*$/;
		$ncpu++;
	}
	$ncpu = 1 if $ncpu == 0;
	return $ncpu;
}

#
# print short usage statement
#
sub usage {
	print <<"END";

abicheck [-h|-?] [-k] [-a] [-I] [-v] [-f <listfile>] [-o <outfile>]
         [-p <pattern>] [-e <pattern>] [-j <njobs>] [-l <library> -l ...]
         [-L <ldpath>] [(-s|-S) <dbfile>] [(-d|-D) <dbfile>] [-O <dbfile>]
         [-A <listfile>] <file> ...

END

}

#
# print long description 
#
sub help {
	if (  ! $ENV{ABICHECK_HELP} ) {	# try to use a pager
		$ENV{ABICHECK_HELP} = 1;
		if ( $ENV{PAGER} ) {
			system("$0 -h | $ENV{PAGER}");
			return;
		} else {
			system("type more 1>/dev/null 2>&1");
			if ( $? == 0 ) {
				system("$0 -h | more");
				return
			}
		}
	}
	usage();
	print <<"END";

abicheck: check application binaries for calls to private or evolving symbols
          in libraries and check for static linking of some system libraries.


abicheck is run on application binaries and issues warnings whenever
any of the following three conditions are detected:

    Private symbol usage.  Private symbols are functions or data
    variables in a library package that are internal to that package.
    They are used by the libraries in the package for internal
    communication and are not part of the API/ABI that application
    developers should use.

    Evolving symbol usage.  Evolving symbols are functions or data
    variables in a library package that are intended for developer
    consumption, but have been marked as "evolving" or "unstable"" in
    the sense that they may become incompatible or disappear on a later
    release of the library package.

    Static linking.  Static linking of system libraries (e.g. libc.a)
    into an application is generally not a good idea because the system
    library code it "locks" into the application binary may become
    incompatible with later releases of the system.  abicheck attempts
    to detect static linking of a few system libraries.  The -s and -S
    options can be used to specify more extensive databases.

    The default behavior is to, for each binary object checked, to
    examine direct calls from that binary object only.  The -l option
    allows the libraries the binary object brings in to have their
    calls checked as well.


Usage:	abicheck <options> <files>

<files> is a list of application binary objects to check.

<options> are:

	-k		Keep on checking binaries even if there are
			serious errors (dynamic linker reports unresolved
			symbols, ldd(1) failures, no symbols detected) 

	-h, -?		Print out long form of help.

	-v		Verbose.  Print out additional information.

	-f <listfile>	A file containing a list of binary objects
			to check, one per line.  This list is appended
			to any list on the cmd line via <files>. 
			If <listfile> is '-', then stdin is used.

	-o <outfile>	Write output to <outfile> instead of stdout.

	-p <pattern>	Modify the version name pattern match labelling
			private version sets.  Default is /private/ using
			a case insensitive match.

			If a component of the regex <pattern> contains two
			colons in a row:  'patt1::patt2', then symbol level
			matching will be activated by checking whether
			"<version>::<symbol>" or "<library>::<symbol>"
			matches <pattern> (where the symbol name, version
			(if any), and library basename are substituted for
			<symbol>, <version>, and <library>).
			E.g.:
				-p 'FOO_VERS.*::_foopriv' or
				-p 'libfoo\.so.*::_foopriv'

	-e <pattern>	Same as -p but for "evolving" interfaces.

	-L <ldpath>	Set the LD_LIBRARY_PATH environment variable to
			<ldpath> before invoking dynamic linker.
			Use -L "" to unset LD_LIBRARY_PATH.

			If one of the components of <ldpath> is the
			string "find", then all shared libraries in
			<files> are found and their paths inserted into
			the "find" location.  Note that the order will 
			random.

	-l <library>	Add the basename or full path to the shared
			object library <library> to the list of objects
			to be checked for making private calls.  This
			option may occur more than once on the command
			line and is additive.  By default, only direct
			calls from a binary to the system libraries are
			checked.  The -l switch allows checking of indirect
			calls e.g.: app -> supportlib -> systemlib.

	-a 		Loop through all of the binaries before checking
			and collect the list of all shared objects.
			Take the basename of each shared object found
			and act as though it was specified with the -l
			option option and then run the abicheck checks.
			This way, calls from all "application internal"
			objects are checked rather than just the direct
			calls.  (Useful when shared objects do not have
			their dependencies recorded.)

	-I		Ignore shared libraries in checking, only
			check executables.  Compatible with -a: libraries
			will be searched for first but then not checked.

	-d <dbfile>	Specify fallback flat-file symbol database for
	-D <dbfile>	the dynamic (public vs. private) test.  These
			classifications will be used if the library is not
			versioned (i.e. classification does not exist
			in the library itself).  Use -D to indicate that
			only information from <dbfile> should be used.
			Lines in <dbfile> can be of one of these forms:

				<library>|<symbol>
				<library>|<class>|<symbol>
				<library>|FILE=<path>

			<library> must be the full path to the library
			to be specified (it cannot be a basename).

			The first form marks <symbol> as private.

			The second form marks <symbol> with <class>
			where <class> may be public, private, or evolving.

			The third form indicates the file <path> should
			be opened on demand when <library> is first
			encountered.  File <path> contains lines of the
			first two forms except for the <library>
			field.  The third form is a speedup to avoid
			processing many classification lines for
			libraries never encountered in the run.

	-O <dbfile>	Specify an override file to modify the symbol
			classification for the dynamic (public vs. private)
			test.  The format for the override file is like:

				<library>|<symbol>|<class>

			The library can be the full path or basename.
			If library is "__SKIP__" the symbol will be ignored
			for any library it is found in.  The class can be
			"public", "private", "evolving", or "deleted".
			The "deleted" class is special-cased, means the 
			symbol was deleted from the library on some release.
			The symbol "__ALL__" for the "deleted" class means
			the entire library was deleted or is otherwise
			unstable to use.

			Examples:

				libfoo.so.1|__bar|private
				/lib/libxyz.so.1|baz|public
				__SKIP__|__fputwc_xpg5

			These settings override any classification
			inside the library (from library versioning,
			obtainable from pvs(1), etc).

	-A <listfile>	Set the ABI libraries of interest to the libraries
			listed in <listfile> (full pathnames, one per line).
			Only calls into these libraries will be checked;
			all other library calls will be ignored.

	-s <dbfile>	Specify more extensive symbol databases for the
	-S <dbfile>	static linking test.  <dbfile> may be a comma
			separated list of files.  If a file is a static
			archive (lib*.a) it is processed to extract the
			symbols.  Otherwise it is a database file consisting
			of lines of the form <symbol>|<library>:<module>
			for example:

				shmat|/usr/lib/libc.a:shmsys.o
				shmctl|/usr/lib/libc.a:shmsys.o
				shmdt|/usr/lib/libc.a:shmsys.o
				shmget|/usr/lib/libc.a:shmsys.o
				...

			When all symbols in a module.o are defined in
			the application, static linking of that module
			(and corresponding library archive) is
			assumed.  Use -S to indicate that only the
			static link test should be performed.

			Use "-S int" to do only the static link check
			and using the internal database.

			Use "-s none" or "-S none" to skip the static
			linking check entirely.

	-j <njobs>	Run <njobs> in parallel as separate processes.
			Implies -k.  Primarily intended for multiple
			CPU machines where <njobs> should be close to
			the number of processors.  Output is collected
			in tmp files and printed all at once near the
			end of the run as each job finishes.

			If <njobs> is "-", "detect", or "n", then njobs
			will be set to a number depending on the number
			of processors on the current machine (if that
			can be determined).

Output:
	There is one line per problem (there may be multiple problems
	per binary checked) which look like the following: 

	  If no problems were found:
	     <filename>: OK

	  If private symbol usage:
	     <filename>: PRIVATE (<library>:<private_version>) <private_sym>

	  If evolving symbol usage:
	     <filename>: EVOLVING (<library>:<evolving_vers>) <evolving_sym>

	  If file statically linked in a system archive library:
	     <filename>: STATIC_LINK <archive> (<n> symbols overlap)

	   (use of the -v option will cause the statically bound symbols
	   to be listed.)

	  If checking of the file was skipped:
	     <filename>: SKIP (<reason>)

	Under use of the deleted class in the -O override file option, these
	problems may be found:

	  If a symbol has been deleted from the library on some release:

	     <filename>: DELETED_SYM: <symbol>/<library>

	  (<library> will be "unbound" if the symbol was unbound)

	  If an entire library has been deleted on some release or is
	  otherwise unstable to use:

	     <filename>: UNSTABLE_LIB: <library-soname> => <library-path>

	  (<library-path> may be "file not found" if the library could not
	  be found)

	The following problems will cause a fatal error unless the -k
	switch is used:

	  If the dynamic linker could not resolve N symbols when ldd -r was run:
	     <filename>: UNBOUND_SYMBOLS: <N>

	  If the dynamic linker found no dynamic bindings:
	     <filename>: NO_BINDINGS

	  If ldd(1) -r with LD_DEBUG=files,bindings failed:
	     <filename>: LDD_ERROR

	In these latter 3 cases run "ldd -r" on the binary file for
	more information on what went wrong.  (abicheck runs ldd -r with
	LD_DEBUG=files,bindings set).  On some systems the dynamic linker
	will not process SUID programs with LD_DEBUG set (this usually
	results in NO_BINDINGS in the abicheck output).

	Note that if you are running abicheck on a shared library
	(e.g. libfoo.so) that has NOT been built with -l<lib> flags
	to record its library dependencies, then the "unbound symbols"
	problem is very likely.  There is not much that can be done besides
	rebuilding the library or checking an application binary that
	uses the library and use the abicheck -l switch.


Exit status:
	0    no errors and no problems found.
	1    a fatal error occurred.
	2    no fatal errors but some binaries with problems were detected.

Notes:
	Only ELF objects are checked.

	In the -s, -S, -d, and -O dbfiles the '#' character starts
	a comment line in the usual way.

	Unless one is using the "::" custom matches supplied via the -p or
	-e flags, abicheck can only check against system libraries that
	have had symbol versioning applied to them (i.e. the private and/or
	evolving information recorded for each symbol in the library
	itself). For more info about symbol versioning, see the
	"Solaris Linker and Libraries Guide" answerbook at the URL
	http://docs.sun.com/ab2/coll.45.13 and the Commands/Version-Script
	section of the GNU linker "ld" info page.

	The default symbol version name matching patterns are case
	insensitive matches to the strings "private" and "evolving" for
	the private and evolving cases, respectively.

	Odd filenames containing the single-quote character or newline
	will be skipped; these characters interfere with calling
	commands via the shell.

	To recurse directories use find(1) and either collect the
	output to a file for use with the -f option, or in a pipe in via:

		find ... | abicheck -f - ...

Bugs:

	The program is dependent on the form of the runtime linkers
	debug output.  Since this output is intended to be human
	readable rather than machine readable, abicheck will break
	whenever the output format changes.  On Solaris it is possible
	that the Link Auditing C interface could be used to avoid this
	problem.

	On Linux when ldd(1) is run on a SUID binary, it (ldd and the
	dynamic-linker) will sometimes actually run the binary. On Linux
	SUID/SGID binaries are currently skipped even if the user is root;
	test unprivileged copies instead.

END

}



#
# Setup some special case symbols to skip the processing of.
# Yes, this is a lot of ugly data... 
#
sub get_special_case_syms {
	my ($public, $evolving, $private, $skip, $archive_skip) = @_;

	# skip undef items introduced by compiler via crt, etc,
	# or known private/evolving versioning errors.

	# false positives in static linking
	if ( $OS eq 'solaris' ) {
		push(@{$archive_skip}, qw(
			malloc.o
			textmem.o
		));
	} elsif ( $OS eq 'linux' ) {
		@{$archive_skip} = qw(
			getopt.o
			snprintf.o
			xmalloc.o
		);
	}

	if ( $OS eq 'solaris' ) {
		#
		# Some low-level symbols we are not interested in.
		#
		@{$skip} = qw(
			_END_
			_START_
			__1cG__CrunMdo_exit_code6F_v_
			__1cG__CrunVdo_exit_code_in_range6Fpv1_v_
			__1cH__CimplKcplus_fini6F_v_
			__1cH__CimplKcplus_init6F_v_
			__fsr_init_value
			_ex_deregister
			_ex_register
			_fini
			_get_exit_frame_monitor
			_init
			_objcInit
			main
			_ctype
			__ctype
			__ctype_mask
			_end
			_environ
			environ
			errno
			__iob
			_lib_version

			__fpstart
			__xtol
			__xtoll
		);

		#
		# These are frequently called symbols that are incorrectly
		# classified in Solaris.
		#
		@{$public} = qw(

			libDtSvc.so.1:DtActionInvoke
			libDtSvc.so.1:DtCreateHelpDialog
			libDtSvc.so.1:DtCreateHelpQuickDialog
			libDtSvc.so.1:DtDtsIsTrue
			libDtSvc.so.1:DtHelpQuickDialogGetChild
			libDtSvc.so.1:DtHelpReturnSelectedWidgetId
			libDtSvc.so.1:DtSaverGetWindows
			libDtSvc.so.1:DtWsmRemoveWorkspaceCallback

			libX11.so.4:XReadBitmapFileData
			libX11.so.4:_XAllocScratch
			libX11.so.4:_XAllocTemp
			libX11.so.4:_XAsyncErrorHandler
			libX11.so.4:_XDeq
			libX11.so.4:_XEatData
			libX11.so.4:_XEnq
			libX11.so.4:_XError
			libX11.so.4:_XErrorFunction
			libX11.so.4:_XEventsQueued
			libX11.so.4:_XFlush
			libX11.so.4:_XFreeTemp
			libX11.so.4:_XGetAsyncReply
			libX11.so.4:_XGetHostname
			libX11.so.4:_XIOError
			libX11.so.4:_XIOErrorFunction
			libX11.so.4:_XRead
			libX11.so.4:_XReadEvents
			libX11.so.4:_XReadPad
			libX11.so.4:_XRegisterInternalConnection
			libX11.so.4:_XReply
			libX11.so.4:_XScreenOfWindow
			libX11.so.4:_XSend
			libX11.so.4:_XSetLastRequestRead
			libX11.so.4:_XUnregisterInternalConnection
			libX11.so.4:_XVIDtoVisual
			libX11.so.4:_Xdebug
			libX11.so.4:_Xglobal_lock

			libXaw.so.5:FMT8BIT

			libXm.so.3:_XmInheritClass
			libXm.so.4:_XmStrings
			libXm.so.4:_XmStrings21

			libXt.so.4:XtGetSelectionParameters
			libXt.so.4:XtOpenApplication
			libXt.so.4:XtR6ShellStrings
			libXt.so.4:XtR6Strings
			libXt.so.4:XtSendSelectionRequest
			libXt.so.4:XtSetSelectionParameters
			libXt.so.4:XtShellStrings
			libXt.so.4:XtStrings
			libXt.so.4:_XLockMutex_fn
			libXt.so.4:_XUnlockMutex_fn
			libXt.so.4:_XtCheckSubclassFlag
			libXt.so.4:_XtInherit
			libXt.so.4:_XtInheritTranslations
			libXt.so.4:_XtIsSubclassOf

			libc.so.1:__fpstart
			libc.so.1:htonl
			libc.so.1:htons
			libc.so.1:mkstemp
			libc.so.1:ntohl
			libc.so.1:ntohs
			libc.so.1:thr_main

			libdmi.so.1:dmi_error
			libdmi.so.1:freeDmiString
			libdmi.so.1:newDmiString
			libdmi.so.1:printDmiDataUnion
			libdmi.so.1:printDmiString

			libnsl.so.1:gethostname

			libresolv.so.2:res_mkupdrec
		);

		@{$private} = qw(
		);

	} elsif ( $OS eq 'linux' ) {
		#
		# Some low-level symbols we are not interested in.
		#
		@{$skip} = qw(
			atexit
			_environ
			etext
			exit
			_fini
			_fp_hw
			_GLOBAL_OFFSET_TABLE_
			__gmon_start__
			_init
			__libc_init_first
			__libc_start_main
			main
			_mcleanup
			__monstartup
			monstartup
		);

		#
		# This big hardwired exception list is pretty gross, but 
		# without it nearly all applications use at least one of
		# the following symbols.  
		#
		# The good news is that it is based on the new GLIBC_PRIVATE
		# version set in GLIBC 2.2.5.  This list was extracted from the
		# GLIBC 2.3 sources using the versioning scripts "Versions".
		# If the symbol name started with "_" but is not in the
		# GLIBC_PRIVATE set, it is assumed private and is listed here.
		#
		# As GLIBC 2.2.5 and above appears more widely in the field
		# we will # remove this list entirely, or keep it as an option.
		#
		@{$public} = qw(
			ld:___tls_get_addr
			ld:__close
			ld:__fxstat
			ld:__getcwd
			ld:__getpid
			ld:__hurd_sigthread_stack_base
			ld:__hurd_sigthread_stack_end
			ld:__hurd_sigthread_variables
			ld:__hurd_threadvar_stack_mask
			ld:__hurd_threadvar_stack_offset
			ld:__libc_memalign
			ld:__libc_read
			ld:__libc_write
			ld:__lseek
			ld:__mmap
			ld:__open
			ld:__tls_get_addr
			ld:__xstat
			ld:_dl_mcount
			ld:_exit
			ld:_hurd_intr_rpc_mach_msg
			ld:_r_debug
			libBrokenLocale:__ctype_get_mb_cur_max
			libc.so.6:_Exit
			libc.so.6:_IO_2_1_stderr_
			libc.so.6:_IO_2_1_stdin_
			libc.so.6:_IO_2_1_stdout_
			libc.so.6:_IO_adjust_column
			libc.so.6:_IO_adjust_wcolumn
			libc.so.6:_IO_clearerr
			libc.so.6:_IO_default_doallocate
			libc.so.6:_IO_default_finish
			libc.so.6:_IO_default_pbackfail
			libc.so.6:_IO_default_uflow
			libc.so.6:_IO_default_xsgetn
			libc.so.6:_IO_default_xsputn
			libc.so.6:_IO_do_write
			libc.so.6:_IO_doallocbuf
			libc.so.6:_IO_fclose
			libc.so.6:_IO_fdopen
			libc.so.6:_IO_feof
			libc.so.6:_IO_ferror
			libc.so.6:_IO_fflush
			libc.so.6:_IO_fgetc
			libc.so.6:_IO_fgetpos
			libc.so.6:_IO_fgetpos64
			libc.so.6:_IO_fgets
			libc.so.6:_IO_file_attach
			libc.so.6:_IO_file_close
			libc.so.6:_IO_file_close_it
			libc.so.6:_IO_file_doallocate
			libc.so.6:_IO_file_finish
			libc.so.6:_IO_file_fopen
			libc.so.6:_IO_file_init
			libc.so.6:_IO_file_jumps
			libc.so.6:_IO_file_open
			libc.so.6:_IO_file_overflow
			libc.so.6:_IO_file_read
			libc.so.6:_IO_file_seek
			libc.so.6:_IO_file_seekoff
			libc.so.6:_IO_file_setbuf
			libc.so.6:_IO_file_stat
			libc.so.6:_IO_file_sync
			libc.so.6:_IO_file_underflow
			libc.so.6:_IO_file_write
			libc.so.6:_IO_file_xsputn
			libc.so.6:_IO_fileno
			libc.so.6:_IO_flockfile
			libc.so.6:_IO_flush_all
			libc.so.6:_IO_flush_all_linebuffered
			libc.so.6:_IO_fopen
			libc.so.6:_IO_fprintf
			libc.so.6:_IO_fputs
			libc.so.6:_IO_fread
			libc.so.6:_IO_free_backup_area
			libc.so.6:_IO_free_wbackup_area
			libc.so.6:_IO_freopen
			libc.so.6:_IO_fscanf
			libc.so.6:_IO_fseek
			libc.so.6:_IO_fsetpos
			libc.so.6:_IO_fsetpos64
			libc.so.6:_IO_ftell
			libc.so.6:_IO_ftrylockfile
			libc.so.6:_IO_funlockfile
			libc.so.6:_IO_fwrite
			libc.so.6:_IO_getc
			libc.so.6:_IO_getline
			libc.so.6:_IO_getline_info
			libc.so.6:_IO_gets
			libc.so.6:_IO_init
			libc.so.6:_IO_init_marker
			libc.so.6:_IO_init_wmarker
			libc.so.6:_IO_iter_begin
			libc.so.6:_IO_iter_end
			libc.so.6:_IO_iter_file
			libc.so.6:_IO_iter_next
			libc.so.6:_IO_least_wmarker
			libc.so.6:_IO_link_in
			libc.so.6:_IO_list_all
			libc.so.6:_IO_list_lock
			libc.so.6:_IO_list_resetlock
			libc.so.6:_IO_list_unlock
			libc.so.6:_IO_marker_delta
			libc.so.6:_IO_marker_difference
			libc.so.6:_IO_padn
			libc.so.6:_IO_pclose
			libc.so.6:_IO_peekc_locked
			libc.so.6:_IO_peekc_unlocked
			libc.so.6:_IO_perror
			libc.so.6:_IO_popen
			libc.so.6:_IO_printf
			libc.so.6:_IO_proc_close
			libc.so.6:_IO_proc_open
			libc.so.6:_IO_putc
			libc.so.6:_IO_puts
			libc.so.6:_IO_remove_marker
			libc.so.6:_IO_rewind
			libc.so.6:_IO_scanf
			libc.so.6:_IO_seekmark
			libc.so.6:_IO_seekoff
			libc.so.6:_IO_seekpos
			libc.so.6:_IO_seekwmark
			libc.so.6:_IO_setb
			libc.so.6:_IO_setbuf
			libc.so.6:_IO_setbuffer
			libc.so.6:_IO_setlinebuf
			libc.so.6:_IO_setvbuf
			libc.so.6:_IO_sgetn
			libc.so.6:_IO_sprintf
			libc.so.6:_IO_sputbackc
			libc.so.6:_IO_sputbackwc
			libc.so.6:_IO_sscanf
			libc.so.6:_IO_stderr_
			libc.so.6:_IO_stdin_
			libc.so.6:_IO_stdout_
			libc.so.6:_IO_str_init_readonly
			libc.so.6:_IO_str_init_static
			libc.so.6:_IO_str_overflow
			libc.so.6:_IO_str_pbackfail
			libc.so.6:_IO_str_seekoff
			libc.so.6:_IO_str_underflow
			libc.so.6:_IO_sungetc
			libc.so.6:_IO_sungetwc
			libc.so.6:_IO_switch_to_get_mode
			libc.so.6:_IO_switch_to_main_wget_area
			libc.so.6:_IO_switch_to_wbackup_area
			libc.so.6:_IO_switch_to_wget_mode
			libc.so.6:_IO_un_link
			libc.so.6:_IO_ungetc
			libc.so.6:_IO_unsave_markers
			libc.so.6:_IO_unsave_wmarkers
			libc.so.6:_IO_vfprintf
			libc.so.6:_IO_vfscanf
			libc.so.6:_IO_vsprintf
			libc.so.6:_IO_wdefault_doallocate
			libc.so.6:_IO_wdefault_finish
			libc.so.6:_IO_wdefault_pbackfail
			libc.so.6:_IO_wdefault_setbuf
			libc.so.6:_IO_wdefault_uflow
			libc.so.6:_IO_wdefault_xsgetn
			libc.so.6:_IO_wdefault_xsputn
			libc.so.6:_IO_wdo_write
			libc.so.6:_IO_wdoallocbuf
			libc.so.6:_IO_wfile_jumps
			libc.so.6:_IO_wfile_overflow
			libc.so.6:_IO_wfile_seekoff
			libc.so.6:_IO_wfile_setbuf
			libc.so.6:_IO_wfile_sync
			libc.so.6:_IO_wfile_underflow
			libc.so.6:_IO_wfile_xsputn
			libc.so.6:_IO_wmarker_delta
			libc.so.6:_IO_wsetb
			libc.so.6:_Qp_add
			libc.so.6:_Qp_cmp
			libc.so.6:_Qp_cmpe
			libc.so.6:_Qp_div
			libc.so.6:_Qp_dtoq
			libc.so.6:_Qp_feq
			libc.so.6:_Qp_fge
			libc.so.6:_Qp_fgt
			libc.so.6:_Qp_fle
			libc.so.6:_Qp_flt
			libc.so.6:_Qp_fne
			libc.so.6:_Qp_itoq
			libc.so.6:_Qp_mul
			libc.so.6:_Qp_neg
			libc.so.6:_Qp_qtod
			libc.so.6:_Qp_qtoi
			libc.so.6:_Qp_qtos
			libc.so.6:_Qp_qtoui
			libc.so.6:_Qp_qtoux
			libc.so.6:_Qp_qtox
			libc.so.6:_Qp_sqrt
			libc.so.6:_Qp_stoq
			libc.so.6:_Qp_sub
			libc.so.6:_Qp_uitoq
			libc.so.6:_Qp_uxtoq
			libc.so.6:_Qp_xtoq
			libc.so.6:_S_catch_exception_raise
			libc.so.6:_S_catch_exception_raise_state
			libc.so.6:_S_catch_exception_raise_state_identity
			libc.so.6:_S_msg_add_auth
			libc.so.6:_S_msg_del_auth
			libc.so.6:_S_msg_describe_ports
			libc.so.6:_S_msg_get_dtable
			libc.so.6:_S_msg_get_env_variable
			libc.so.6:_S_msg_get_environment
			libc.so.6:_S_msg_get_fd
			libc.so.6:_S_msg_get_init_int
			libc.so.6:_S_msg_get_init_ints
			libc.so.6:_S_msg_get_init_port
			libc.so.6:_S_msg_get_init_ports
			libc.so.6:_S_msg_proc_newids
			libc.so.6:_S_msg_report_wait
			libc.so.6:_S_msg_set_dtable
			libc.so.6:_S_msg_set_env_variable
			libc.so.6:_S_msg_set_environment
			libc.so.6:_S_msg_set_fd
			libc.so.6:_S_msg_set_init_int
			libc.so.6:_S_msg_set_init_ints
			libc.so.6:_S_msg_set_init_port
			libc.so.6:_S_msg_set_init_ports
			libc.so.6:_S_msg_sig_post
			libc.so.6:_S_msg_sig_post_untraced
			libc.so.6:__Qp_handle_exceptions
			libc.so.6:___brk_addr
			libc.so.6:__adjtimex
			libc.so.6:__after_morecore_hook
			libc.so.6:__align_cpy_1
			libc.so.6:__align_cpy_16
			libc.so.6:__align_cpy_2
			libc.so.6:__align_cpy_4
			libc.so.6:__align_cpy_8
			libc.so.6:__arch_prctl
			libc.so.6:__argz_count
			libc.so.6:__argz_next
			libc.so.6:__argz_stringify
			libc.so.6:__asprintf
			libc.so.6:__assert
			libc.so.6:__assert_fail
			libc.so.6:__assert_perror_fail
			libc.so.6:__backtrace
			libc.so.6:__backtrace_symbols
			libc.so.6:__backtrace_symbols_fd
			libc.so.6:__bsd_getpgrp
			libc.so.6:__bzero
			libc.so.6:__check_rhosts_file
			libc.so.6:__clone
			libc.so.6:__clone2
			libc.so.6:__close
			libc.so.6:__cmsg_nxthdr
			libc.so.6:__connect
			libc.so.6:__ctype32_b
			libc.so.6:__ctype32_tolower
			libc.so.6:__ctype32_toupper
			libc.so.6:__ctype_b
			libc.so.6:__ctype_get_mb_cur_max
			libc.so.6:__ctype_tolower
			libc.so.6:__ctype_toupper
			libc.so.6:__curbrk
			libc.so.6:__cxa_atexit
			libc.so.6:__cxa_finalize
			libc.so.6:__cyg_profile_func_enter
			libc.so.6:__cyg_profile_func_exit
			libc.so.6:__daylight
			libc.so.6:__dcgettext
			libc.so.6:__dcngettext
			libc.so.6:__default_morecore
			libc.so.6:__deregister_frame
			libc.so.6:__deregister_frame_info
			libc.so.6:__dgettext
			libc.so.6:__divdi3
			libc.so.6:__divl
			libc.so.6:__divls
			libc.so.6:__divlu
			libc.so.6:__divq
			libc.so.6:__divqs
			libc.so.6:__divqu
			libc.so.6:__dup2
			libc.so.6:__duplocale
			libc.so.6:__endmntent
			libc.so.6:__environ
			libc.so.6:__errno_location
			libc.so.6:__fbufsize
			libc.so.6:__fcntl
			libc.so.6:__ffs
			libc.so.6:__fillbf
			libc.so.6:__finite
			libc.so.6:__finitef
			libc.so.6:__finitel
			libc.so.6:__flbf
			libc.so.6:__flshfp
			libc.so.6:__fork
			libc.so.6:__fpending
			libc.so.6:__fpu_control
			libc.so.6:__fpurge
			libc.so.6:__frame_state_for
			libc.so.6:__freadable
			libc.so.6:__freading
			libc.so.6:__free_hook
			libc.so.6:__freelocale
			libc.so.6:__fsetlocking
			libc.so.6:__fwritable
			libc.so.6:__fwriting
			libc.so.6:__fxstat
			libc.so.6:__fxstat64
			libc.so.6:__getcwd
			libc.so.6:__getdelim
			libc.so.6:__getmntent_r
			libc.so.6:__getpagesize
			libc.so.6:__getpgid
			libc.so.6:__getpid
			libc.so.6:__gettimeofday
			libc.so.6:__getuids
			libc.so.6:__gmtime_r
			libc.so.6:__h_errno_location
			libc.so.6:__hurd_errno_location
			libc.so.6:__hurd_fail
			libc.so.6:__hurd_file_name_lookup
			libc.so.6:__hurd_sigthread_stack_base
			libc.so.6:__hurd_sigthread_stack_end
			libc.so.6:__hurd_sigthread_variables
			libc.so.6:__hurd_threadvar_max
			libc.so.6:__hurd_threadvar_stack_mask
			libc.so.6:__hurd_threadvar_stack_offset
			libc.so.6:__ieee_get_fp_control
			libc.so.6:__ieee_set_fp_control
			libc.so.6:__isalnum_l
			libc.so.6:__isalpha_l
			libc.so.6:__isascii_l
			libc.so.6:__isblank_l
			libc.so.6:__iscntrl_l
			libc.so.6:__isdigit_l
			libc.so.6:__isgraph_l
			libc.so.6:__isinf
			libc.so.6:__isinff
			libc.so.6:__isinfl
			libc.so.6:__islower_l
			libc.so.6:__isnan
			libc.so.6:__isnanf
			libc.so.6:__isnanl
			libc.so.6:__isprint_l
			libc.so.6:__ispunct_l
			libc.so.6:__isspace_l
			libc.so.6:__isupper_l
			libc.so.6:__iswalnum_l
			libc.so.6:__iswalpha_l
			libc.so.6:__iswblank_l
			libc.so.6:__iswcntrl_l
			libc.so.6:__iswctype
			libc.so.6:__iswctype_l
			libc.so.6:__iswdigit_l
			libc.so.6:__iswgraph_l
			libc.so.6:__iswlower_l
			libc.so.6:__iswprint_l
			libc.so.6:__iswpunct_l
			libc.so.6:__iswspace_l
			libc.so.6:__iswupper_l
			libc.so.6:__iswxdigit_l
			libc.so.6:__isxdigit_l
			libc.so.6:__ivaliduser
			libc.so.6:__key_decryptsession_pk_LOCAL
			libc.so.6:__key_encryptsession_pk_LOCAL
			libc.so.6:__key_gendes_LOCAL
			libc.so.6:__libc_allocate_rtsig
			libc.so.6:__libc_calloc
			libc.so.6:__libc_current_sigrtmax
			libc.so.6:__libc_current_sigrtmin
			libc.so.6:__libc_free
			libc.so.6:__libc_freeres
			libc.so.6:__libc_getspecific
			libc.so.6:__libc_init_first
			libc.so.6:__libc_mallinfo
			libc.so.6:__libc_malloc
			libc.so.6:__libc_mallopt
			libc.so.6:__libc_memalign
			libc.so.6:__libc_pvalloc
			libc.so.6:__libc_realloc
			libc.so.6:__libc_sa_len
			libc.so.6:__libc_start_main
			libc.so.6:__libc_valloc
			libc.so.6:__line_wrap_output
			libc.so.6:__line_wrap_update
			libc.so.6:__lseek
			libc.so.6:__lxstat
			libc.so.6:__lxstat64
			libc.so.6:__mach_msg
			libc.so.6:__mach_msg_destroy
			libc.so.6:__mach_msg_overwrite
			libc.so.6:__mach_port_deallocate
			libc.so.6:__mach_reply_port
			libc.so.6:__mach_task_self_
			libc.so.6:__mach_thread_self
			libc.so.6:__malloc_hook
			libc.so.6:__malloc_initialize_hook
			libc.so.6:__malloc_initialized
			libc.so.6:__mbrlen
			libc.so.6:__mbrtowc
			libc.so.6:__memalign_hook
			libc.so.6:__memcpy_by2
			libc.so.6:__memcpy_by4
			libc.so.6:__memcpy_c
			libc.so.6:__memcpy_g
			libc.so.6:__mempcpy
			libc.so.6:__mempcpy_by2
			libc.so.6:__mempcpy_by4
			libc.so.6:__mempcpy_byn
			libc.so.6:__mempcpy_small
			libc.so.6:__memset_cc
			libc.so.6:__memset_ccn_by2
			libc.so.6:__memset_ccn_by4
			libc.so.6:__memset_cg
			libc.so.6:__memset_gcn_by2
			libc.so.6:__memset_gcn_by4
			libc.so.6:__memset_gg
			libc.so.6:__mig_allocate
			libc.so.6:__mig_dealloc_reply_port
			libc.so.6:__mig_deallocate
			libc.so.6:__mig_get_reply_port
			libc.so.6:__mig_put_reply_port
			libc.so.6:__mig_strncpy
			libc.so.6:__mmap
			libc.so.6:__moddi3
			libc.so.6:__monstartup
			libc.so.6:__morecore
			libc.so.6:__mutex_init
			libc.so.6:__mutex_lock
			libc.so.6:__mutex_lock_solid
			libc.so.6:__mutex_trylock
			libc.so.6:__mutex_unlock
			libc.so.6:__mutex_unlock_solid
			libc.so.6:__newlocale
			libc.so.6:__nl_langinfo_l
			libc.so.6:__nss_configure_lookup
			libc.so.6:__nss_database_lookup
			libc.so.6:__nss_group_lookup
			libc.so.6:__nss_hostname_digits_dots
			libc.so.6:__nss_hosts_lookup
			libc.so.6:__nss_next
			libc.so.6:__nss_passwd_lookup
			libc.so.6:__open
			libc.so.6:__open64
			libc.so.6:__overflow
			libc.so.6:__pipe
			libc.so.6:__poll
			libc.so.6:__pread64
			libc.so.6:__printf_fp
			libc.so.6:__profile_frequency
			libc.so.6:__progname
			libc.so.6:__progname_full
			libc.so.6:__pwrite64
			libc.so.6:__rawmemchr
			libc.so.6:__rcmd_errstr
			libc.so.6:__read
			libc.so.6:__realloc_hook
			libc.so.6:__register_frame
			libc.so.6:__register_frame_info
			libc.so.6:__register_frame_info_table
			libc.so.6:__register_frame_table
			libc.so.6:__reml
			libc.so.6:__remls
			libc.so.6:__remlu
			libc.so.6:__remq
			libc.so.6:__remqs
			libc.so.6:__remqu
			libc.so.6:__res_init
			libc.so.6:__res_nclose
			libc.so.6:__res_ninit
			libc.so.6:__res_randomid
			libc.so.6:__res_state
			libc.so.6:__rpc_thread_createerr
			libc.so.6:__rpc_thread_svc_fdset
			libc.so.6:__rpc_thread_svc_max_pollfd
			libc.so.6:__rpc_thread_svc_pollfd
			libc.so.6:__sbrk
			libc.so.6:__sched_get_priority_max
			libc.so.6:__sched_get_priority_min
			libc.so.6:__sched_getparam
			libc.so.6:__sched_getscheduler
			libc.so.6:__sched_setscheduler
			libc.so.6:__sched_yield
			libc.so.6:__secure_getenv
			libc.so.6:__select
			libc.so.6:__send
			libc.so.6:__setmntent
			libc.so.6:__setpgid
			libc.so.6:__sigaction
			libc.so.6:__sigaddset
			libc.so.6:__sigdelset
			libc.so.6:__sigismember
			libc.so.6:__signbit
			libc.so.6:__signbitf
			libc.so.6:__signbitl
			libc.so.6:__sigpause
			libc.so.6:__sigsetjmp
			libc.so.6:__sigsuspend
			libc.so.6:__spin_lock
			libc.so.6:__spin_lock_init
			libc.so.6:__spin_lock_solid
			libc.so.6:__spin_try_lock
			libc.so.6:__spin_unlock
			libc.so.6:__statfs
			libc.so.6:__stpcpy
			libc.so.6:__stpcpy_g
			libc.so.6:__stpcpy_small
			libc.so.6:__stpncpy
			libc.so.6:__strcasecmp
			libc.so.6:__strcasecmp_l
			libc.so.6:__strcasestr
			libc.so.6:__strcat_c
			libc.so.6:__strcat_g
			libc.so.6:__strchr_c
			libc.so.6:__strchr_g
			libc.so.6:__strchrnul_c
			libc.so.6:__strchrnul_g
			libc.so.6:__strcmp_gg
			libc.so.6:__strcoll_l
			libc.so.6:__strcpy_g
			libc.so.6:__strcpy_small
			libc.so.6:__strcspn_c1
			libc.so.6:__strcspn_c2
			libc.so.6:__strcspn_c3
			libc.so.6:__strcspn_cg
			libc.so.6:__strcspn_g
			libc.so.6:__strdup
			libc.so.6:__strerror_r
			libc.so.6:__strfmon_l
			libc.so.6:__strlen_g
			libc.so.6:__strncasecmp_l
			libc.so.6:__strncat_g
			libc.so.6:__strncmp_g
			libc.so.6:__strncpy_by2
			libc.so.6:__strncpy_by4
			libc.so.6:__strncpy_byn
			libc.so.6:__strncpy_gg
			libc.so.6:__strndup
			libc.so.6:__strpbrk_c2
			libc.so.6:__strpbrk_c3
			libc.so.6:__strpbrk_cg
			libc.so.6:__strpbrk_g
			libc.so.6:__strrchr_c
			libc.so.6:__strrchr_g
			libc.so.6:__strsep_1c
			libc.so.6:__strsep_2c
			libc.so.6:__strsep_3c
			libc.so.6:__strsep_g
			libc.so.6:__strspn_c1
			libc.so.6:__strspn_c2
			libc.so.6:__strspn_c3
			libc.so.6:__strspn_cg
			libc.so.6:__strspn_g
			libc.so.6:__strstr_cg
			libc.so.6:__strstr_g
			libc.so.6:__strtod_internal
			libc.so.6:__strtod_l
			libc.so.6:__strtof_internal
			libc.so.6:__strtof_l
			libc.so.6:__strtok_r
			libc.so.6:__strtok_r_1c
			libc.so.6:__strtol_internal
			libc.so.6:__strtol_l
			libc.so.6:__strtold_internal
			libc.so.6:__strtold_l
			libc.so.6:__strtoll_internal
			libc.so.6:__strtoll_l
			libc.so.6:__strtoq_internal
			libc.so.6:__strtoul_internal
			libc.so.6:__strtoul_l
			libc.so.6:__strtoull_internal
			libc.so.6:__strtoull_l
			libc.so.6:__strtouq_internal
			libc.so.6:__strverscmp
			libc.so.6:__strxfrm_l
			libc.so.6:__sysconf
			libc.so.6:__sysctl
			libc.so.6:__sysv_signal
			libc.so.6:__timezone
			libc.so.6:__toascii_l
			libc.so.6:__tolower_l
			libc.so.6:__toupper_l
			libc.so.6:__towctrans
			libc.so.6:__towctrans_l
			libc.so.6:__towlower_l
			libc.so.6:__towupper_l
			libc.so.6:__tzname
			libc.so.6:__udivdi3
			libc.so.6:__uflow
			libc.so.6:__umoddi3
			libc.so.6:__underflow
			libc.so.6:__vfork
			libc.so.6:__vfscanf
			libc.so.6:__vm_allocate
			libc.so.6:__vm_page_size
			libc.so.6:__vsnprintf
			libc.so.6:__vsscanf
			libc.so.6:__wait
			libc.so.6:__waitpid
			libc.so.6:__wcscasecmp_l
			libc.so.6:__wcscoll_l
			libc.so.6:__wcsncasecmp_l
			libc.so.6:__wcstod_internal
			libc.so.6:__wcstod_l
			libc.so.6:__wcstof_internal
			libc.so.6:__wcstof_l
			libc.so.6:__wcstol_internal
			libc.so.6:__wcstol_l
			libc.so.6:__wcstold_internal
			libc.so.6:__wcstold_l
			libc.so.6:__wcstoll_internal
			libc.so.6:__wcstoll_l
			libc.so.6:__wcstoul_internal
			libc.so.6:__wcstoul_l
			libc.so.6:__wcstoull_internal
			libc.so.6:__wcstoull_l
			libc.so.6:__wcsxfrm_l
			libc.so.6:__wctrans_l
			libc.so.6:__wctype_l
			libc.so.6:__woverflow
			libc.so.6:__write
			libc.so.6:__wuflow
			libc.so.6:__wunderflow
			libc.so.6:__xmknod
			libc.so.6:__xpg_basename
			libc.so.6:__xpg_sigpause
			libc.so.6:__xstat
			libc.so.6:__xstat64
			libc.so.6:_argp_unlock_xxx
			libc.so.6:_authenticate
			libc.so.6:_bus_base
			libc.so.6:_bus_base_sparse
			libc.so.6:_cthread_exit_routine
			libc.so.6:_cthread_init_routine
			libc.so.6:_dl_mcount_wrapper
			libc.so.6:_dl_mcount_wrapper_check
			libc.so.6:_end
			libc.so.6:_environ
			libc.so.6:_errno
			libc.so.6:_exit
			libc.so.6:_flush_cache
			libc.so.6:_flushlbf
			libc.so.6:_fp_hw
			libc.so.6:_h_errno
			libc.so.6:_hae_shift
			libc.so.6:_hurd_canonicalize_directory_name_internal
			libc.so.6:_hurd_critical_section_lock
			libc.so.6:_hurd_critical_section_unlock
			libc.so.6:_hurd_device_master
			libc.so.6:_hurd_dtable
			libc.so.6:_hurd_dtable_lock
			libc.so.6:_hurd_dtablesize
			libc.so.6:_hurd_exception2signal
			libc.so.6:_hurd_exec
			libc.so.6:_hurd_fd_get
			libc.so.6:_hurd_host_priv
			libc.so.6:_hurd_init
			libc.so.6:_hurd_intern_fd
			libc.so.6:_hurd_intr_rpc_mach_msg
			libc.so.6:_hurd_msgport
			libc.so.6:_hurd_port_cleanup
			libc.so.6:_hurd_port_free
			libc.so.6:_hurd_port_get
			libc.so.6:_hurd_port_init
			libc.so.6:_hurd_port_locked_get
			libc.so.6:_hurd_port_set
			libc.so.6:_hurd_ports
			libc.so.6:_hurd_ports_use
			libc.so.6:_hurd_proc_init
			libc.so.6:_hurd_raise_signal
			libc.so.6:_hurd_self_sigstate
			libc.so.6:_hurd_thread_sigstate
			libc.so.6:_hurd_umask
			libc.so.6:_hurdsig_fault_catch_exception_raise
			libc.so.6:_hurdsig_fault_catch_exception_raise_state
			libc.so.6:_hurdsig_fault_catch_exception_raise_state_identity
			libc.so.6:_hurdsig_fault_env
			libc.so.6:_hurdsig_fault_preemptor
			libc.so.6:_hurdsig_interrupt_timeout
			libc.so.6:_inb
			libc.so.6:_inl
			libc.so.6:_inw
			libc.so.6:_libc_intl_domainname
			libc.so.6:_longjmp
			libc.so.6:_mcleanup
			libc.so.6:_mcount
			libc.so.6:_nl_default_dirname
			libc.so.6:_nl_domain_bindings
			libc.so.6:_nl_msg_cat_cntr
			libc.so.6:_null_auth
			libc.so.6:_obstack
			libc.so.6:_obstack_allocated_p
			libc.so.6:_obstack_begin
			libc.so.6:_obstack_begin_1
			libc.so.6:_obstack_free
			libc.so.6:_obstack_memory_used
			libc.so.6:_obstack_newchunk
			libc.so.6:_outb
			libc.so.6:_outl
			libc.so.6:_outw
			libc.so.6:_q_add
			libc.so.6:_q_cmp
			libc.so.6:_q_cmpe
			libc.so.6:_q_div
			libc.so.6:_q_dtoq
			libc.so.6:_q_feq
			libc.so.6:_q_fge
			libc.so.6:_q_fgt
			libc.so.6:_q_fle
			libc.so.6:_q_flt
			libc.so.6:_q_fne
			libc.so.6:_q_itoq
			libc.so.6:_q_lltoq
			libc.so.6:_q_mul
			libc.so.6:_q_neg
			libc.so.6:_q_qtod
			libc.so.6:_q_qtoi
			libc.so.6:_q_qtoll
			libc.so.6:_q_qtos
			libc.so.6:_q_qtou
			libc.so.6:_q_qtoull
			libc.so.6:_q_sqrt
			libc.so.6:_q_stoq
			libc.so.6:_q_sub
			libc.so.6:_q_ulltoq
			libc.so.6:_q_utoq
			libc.so.6:_res
			libc.so.6:_res_hconf
			libc.so.6:_rpc_dtablesize
			libc.so.6:_seterr_reply
			libc.so.6:_setjmp
			libc.so.6:_sys_errlist
			libc.so.6:_sys_nerr
			libc.so.6:_sys_siglist
			libc.so.6:_test_and_set
			libc.so.6:_tolower
			libc.so.6:_toupper
			libm.so.6:_LIB_VERSION
			libm.so.6:__atan2
			libm.so.6:__clog10
			libm.so.6:__clog10f
			libm.so.6:__clog10l
			libm.so.6:__expl
			libm.so.6:__expm1l
			libm.so.6:__fe_dfl_env
			libm.so.6:__fe_enabled_env
			libm.so.6:__fe_nomask_env
			libm.so.6:__fe_nonieee_env
			libm.so.6:__finite
			libm.so.6:__finitef
			libm.so.6:__finitel
			libm.so.6:__fpclassify
			libm.so.6:__fpclassifyf
			libm.so.6:__fpclassifyl
			libm.so.6:__signbit
			libm.so.6:__signbitf
			libm.so.6:__signbitl
			libm.so.6:_restgpr0_13
			libm.so.6:_restgpr0_14
			libm.so.6:_restgpr0_15
			libm.so.6:_restgpr0_16
			libm.so.6:_restgpr0_17
			libm.so.6:_restgpr0_18
			libm.so.6:_restgpr0_19
			libm.so.6:_restgpr0_20
			libm.so.6:_restgpr0_21
			libm.so.6:_restgpr0_22
			libm.so.6:_restgpr0_23
			libm.so.6:_restgpr0_24
			libm.so.6:_restgpr0_25
			libm.so.6:_restgpr0_26
			libm.so.6:_restgpr0_27
			libm.so.6:_restgpr0_28
			libm.so.6:_restgpr0_29
			libm.so.6:_restgpr0_30
			libm.so.6:_restgpr0_31
			libm.so.6:_restgpr1_13
			libm.so.6:_restgpr1_14
			libm.so.6:_restgpr1_15
			libm.so.6:_restgpr1_16
			libm.so.6:_restgpr1_17
			libm.so.6:_restgpr1_18
			libm.so.6:_restgpr1_19
			libm.so.6:_restgpr1_20
			libm.so.6:_restgpr1_21
			libm.so.6:_restgpr1_22
			libm.so.6:_restgpr1_23
			libm.so.6:_restgpr1_24
			libm.so.6:_restgpr1_25
			libm.so.6:_restgpr1_26
			libm.so.6:_restgpr1_27
			libm.so.6:_restgpr1_28
			libm.so.6:_restgpr1_29
			libm.so.6:_restgpr1_30
			libm.so.6:_restgpr1_31
			libm.so.6:_savegpr0_13
			libm.so.6:_savegpr0_14
			libm.so.6:_savegpr0_15
			libm.so.6:_savegpr0_16
			libm.so.6:_savegpr0_17
			libm.so.6:_savegpr0_18
			libm.so.6:_savegpr0_19
			libm.so.6:_savegpr0_20
			libm.so.6:_savegpr0_21
			libm.so.6:_savegpr0_22
			libm.so.6:_savegpr0_23
			libm.so.6:_savegpr0_24
			libm.so.6:_savegpr0_25
			libm.so.6:_savegpr0_26
			libm.so.6:_savegpr0_27
			libm.so.6:_savegpr0_28
			libm.so.6:_savegpr0_29
			libm.so.6:_savegpr0_30
			libm.so.6:_savegpr0_31
			libm.so.6:_savegpr1_13
			libm.so.6:_savegpr1_14
			libm.so.6:_savegpr1_15
			libm.so.6:_savegpr1_16
			libm.so.6:_savegpr1_17
			libm.so.6:_savegpr1_18
			libm.so.6:_savegpr1_19
			libm.so.6:_savegpr1_20
			libm.so.6:_savegpr1_21
			libm.so.6:_savegpr1_22
			libm.so.6:_savegpr1_23
			libm.so.6:_savegpr1_24
			libm.so.6:_savegpr1_25
			libm.so.6:_savegpr1_26
			libm.so.6:_savegpr1_27
			libm.so.6:_savegpr1_28
			libm.so.6:_savegpr1_29
			libm.so.6:_savegpr1_30
			libm.so.6:_savegpr1_31
			libnsl.so.1:__free_fdresult
			libnsl.so.1:__nis_default_access
			libnsl.so.1:__nis_default_group
			libnsl.so.1:__nis_default_owner
			libnsl.so.1:__nis_default_ttl
			libnsl.so.1:__nis_finddirectory
			libnsl.so.1:__nis_hash
			libnsl.so.1:__nisbind_connect
			libnsl.so.1:__nisbind_create
			libnsl.so.1:__nisbind_destroy
			libnsl.so.1:__nisbind_next
			libnsl.so.1:__yp_check
			libpthread.so.0:_IO_flockfile
			libpthread.so.0:_IO_ftrylockfile
			libpthread.so.0:_IO_funlockfile
			libpthread.so.0:__close
			libpthread.so.0:__connect
			libpthread.so.0:__errno_location
			libpthread.so.0:__fcntl
			libpthread.so.0:__fork
			libpthread.so.0:__h_errno_location
			libpthread.so.0:__libc_allocate_rtsig
			libpthread.so.0:__libc_current_sigrtmax
			libpthread.so.0:__libc_current_sigrtmin
			libpthread.so.0:__lseek
			libpthread.so.0:__open
			libpthread.so.0:__open64
			libpthread.so.0:__pread64
			libpthread.so.0:__pthread_atfork
			libpthread.so.0:__pthread_getspecific
			libpthread.so.0:__pthread_initialize
			libpthread.so.0:__pthread_key_create
			libpthread.so.0:__pthread_mutex_destroy
			libpthread.so.0:__pthread_mutex_init
			libpthread.so.0:__pthread_mutex_lock
			libpthread.so.0:__pthread_mutex_trylock
			libpthread.so.0:__pthread_mutex_unlock
			libpthread.so.0:__pthread_mutexattr_destroy
			libpthread.so.0:__pthread_mutexattr_init
			libpthread.so.0:__pthread_mutexattr_settype
			libpthread.so.0:__pthread_once
			libpthread.so.0:__pthread_rwlock_destroy
			libpthread.so.0:__pthread_rwlock_init
			libpthread.so.0:__pthread_rwlock_rdlock
			libpthread.so.0:__pthread_rwlock_tryrdlock
			libpthread.so.0:__pthread_rwlock_trywrlock
			libpthread.so.0:__pthread_rwlock_unlock
			libpthread.so.0:__pthread_rwlock_wrlock
			libpthread.so.0:__pthread_setspecific
			libpthread.so.0:__pwrite64
			libpthread.so.0:__read
			libpthread.so.0:__res_state
			libpthread.so.0:__send
			libpthread.so.0:__sigaction
			libpthread.so.0:__vfork
			libpthread.so.0:__wait
			libpthread.so.0:__write
			libpthread.so.0:_pthread_cleanup_pop
			libpthread.so.0:_pthread_cleanup_pop_restore
			libpthread.so.0:_pthread_cleanup_push
			libpthread.so.0:_pthread_cleanup_push_defer
			libresolv.so.2:__b64_ntop
			libresolv.so.2:__b64_pton
			libresolv.so.2:__dn_comp
			libresolv.so.2:__dn_count_labels
			libresolv.so.2:__dn_expand
			libresolv.so.2:__dn_skipname
			libresolv.so.2:__fp_nquery
			libresolv.so.2:__fp_query
			libresolv.so.2:__fp_resstat
			libresolv.so.2:__hostalias
			libresolv.so.2:__loc_aton
			libresolv.so.2:__loc_ntoa
			libresolv.so.2:__p_cdname
			libresolv.so.2:__p_cdnname
			libresolv.so.2:__p_class
			libresolv.so.2:__p_class_syms
			libresolv.so.2:__p_fqname
			libresolv.so.2:__p_fqnname
			libresolv.so.2:__p_option
			libresolv.so.2:__p_query
			libresolv.so.2:__p_rr
			libresolv.so.2:__p_secstodate
			libresolv.so.2:__p_time
			libresolv.so.2:__p_type
			libresolv.so.2:__p_type_syms
			libresolv.so.2:__putlong
			libresolv.so.2:__putshort
			libresolv.so.2:__res_close
			libresolv.so.2:__res_dnok
			libresolv.so.2:__res_hnok
			libresolv.so.2:__res_hostalias
			libresolv.so.2:__res_isourserver
			libresolv.so.2:__res_mailok
			libresolv.so.2:__res_mkquery
			libresolv.so.2:__res_nameinquery
			libresolv.so.2:__res_nmkquery
			libresolv.so.2:__res_nquery
			libresolv.so.2:__res_nquerydomain
			libresolv.so.2:__res_nsearch
			libresolv.so.2:__res_nsend
			libresolv.so.2:__res_ownok
			libresolv.so.2:__res_queriesmatch
			libresolv.so.2:__res_query
			libresolv.so.2:__res_querydomain
			libresolv.so.2:__res_search
			libresolv.so.2:__res_send
			libresolv.so.2:__sym_ntop
			libresolv.so.2:__sym_ntos
			libresolv.so.2:__sym_ston
			libresolv.so.2:_gethtbyaddr
			libresolv.so.2:_gethtbyname
			libresolv.so.2:_gethtbyname2
			libresolv.so.2:_gethtent
			libresolv.so.2:_getlong
			libresolv.so.2:_getshort
			libresolv.so.2:_res_opcodes
			libresolv.so.2:_res_resultcodes
			libresolv.so.2:_sethtent

			libc.so.6:__ctype_b_loc
			libc.so.6:__ctype_tolower_loc
			libc.so.6:__ctype_toupper_loc
		);

		@{$private} = qw(
		);

		#
		# These are  are from older libraries.
		#
		push(@{$public}, qw(
			libdb.so.3:__bam_init_print
			libdb.so.3:__bam_pgin
			libdb.so.3:__bam_pgout
			libdb.so.3:__db_dispatch
			libdb.so.3:__db_dump
			libdb.so.3:__db_err
			libdb.so.3:__db_init_print
			libdb.so.3:__db_jump
			libdb.so.3:__db_omode
			libdb.so.3:__db_prdbt
			libdb.so.3:__ham_init_print
			libdb.so.3:__ham_pgin
			libdb.so.3:__ham_pgout
			libdb.so.3:__lock_dump_region
			libdb.so.3:__log_init_print
			libdb.so.3:__memp_dump_region
			libdb.so.3:__txn_init_print
			libresolv.so.2:__dn_skipname
			)
		);

		#
		# These are non-leading "_" that are actually private:
		# If one reads the comment in nis/Versions it strongly
		# suggests they are private, even though they are in
		# GLIBC_2.1 set.  We keep them as tweaks for now.
		#
		@{$private} = qw(
			libnsl.so.1:readColdStartFile
			libnsl.so.1:writeColdStartFile
		);
	}
}
