Subject: [SCRIPT] find-dll-symbol
From: Joe Buehler <[email-masked]>
To: [email-masked]
Date: Tue, 17 Dec 2002 09:00:08 -0500

The attached script is an improved version of a script previously
posted.  This one takes hex numbers on the command line and dumps
the associated dll symbol information.  It is useful in determining
what functions are present in a stack trace.

The dll information is cached.  Use --update to update the cache
(dll modify times are checked) or --rebuild to rebuild the cache
completely (all dll files are reexamined).

Change $CACHEFILE and @DLLDIRS to suit.

Joe Buehler

#!/usr/bin/perl
#
# find symbol and dll given an address
#
# You need "dumpbin" for this to work.
#
# Set @DLLDIRS to the list of directories in which you are interested.
# Set $CACHEFILE to the location of the cache file.
#
# coded by Joe Buehler ([email-masked])
#
# $Id: find-dll-symbol,v 1.1 2002/12/17 13:56:08 jhpb Exp $
#

$CACHEFILE = "/usr/local/lib/dll-info";
@DLLDIRS = ("/bin", "/sys");

my @SYMBOL_INFO;

update_symbol_info();

for $arg (@ARGV) {
	if ($arg eq "--rebuild") {
		unlink($CACHEFILE);
		update_symbol_info(1);
	} elsif ($arg eq "--update") {
		update_symbol_info(1);
	} elsif ($arg =~ /^-/o) {
		print STDERR "$0: $arg: unknown option\n";
		exit(1);
	} else {
		find_symbol($arg);
	}
}

sub update_symbol_info
{
	my($force_rebuild) = @_;

	my %dll_info;
	my $modified = 0;
	my $mtime_cache = 0;
	my $cache_was_read = 0;

	# don't do this if we have already done it
	return if (@SYMBOL_INFO && !$force_rebuild);

	# read in the cache file if it exists
	if (-f $CACHEFILE) {

		# get modify time for cache file
		my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
		 $atime,$mtime,$ctime,$blksize,$blocks) = stat(_);
		$mtime_cache = $mtime;

		# read in cache file
		open(CACHE, "<$CACHEFILE");
		while (<CACHE>) {
			chomp;
			my($start, $dll, $name) = split(/:/, $_, 3);
			push(@{$dll_info{$dll}}, [hex($start), $dll, $name]);
			push(@SYMBOL_INFO, [hex($start), $dll, $name]);
		}
		close(CACHE);

		return unless $force_rebuild;

		++$cache_was_read;
	}

	print STDERR "rebuilding cache...\n";

	# update files for which there is no data in the cache or it is out of date
	for $dlldir (@DLLDIRS) {
		next unless -d "$dlldir";
		opendir(DLLDIR, $dlldir);
		while ($dll = readdir(DLLDIR)) {
			next unless $dll =~ /[.]dll$/io;
			next unless -f "$dlldir/$dll";

			# normalize dll file name
			$dll = lc $dll;

			# check dll modify time vs. cache file
			($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
			 $atime,$mtime,$ctime,$blksize,$blocks) = stat(_);

			next if ((exists $dll_info{$dll}) && ($mtime < $mtime_cache));

			print STDERR "*** updating $dlldir/$dll\n";

			$modified = 1;

			delete $dll_info{$dll};
			push(@{$dll_info{$dll}}, [0, $dll, ""]);

			open(DUMPBIN, qq{ cd '$dlldir' && dumpbin /headers /exports '$dll' | });
			while (<DUMPBIN>) {
				# remove line endings
				s/\r*\n//o;
				# discard blank lines
				next if /^\s*$/o;
				# process stuff in which we are interested
				if (/^\s*(\S+)\s+image\s+base\s*$/io) {
					$image_base_address = hex $1;
				} elsif (/^\s+ordinal\s+hint\s+RVA\s+name\s*$/io .. /^\S/o) {
					next if /^\s+ordinal\s+hint\s+RVA\s+name\s*$/io;
					next if /^\S/io;
					next unless ($ordinal, $hint, $RVA, $name) =
						/^\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)$/o;
					push(@{$dll_info{$dll}}, [$image_base_address + hex($RVA), $dll, $name]);
				}
			}
			close(DUMPBIN);
		}
		closedir(DLLDIR);
	}

	if ($modified) {
		undef @SYMBOL_INFO;
		for $info (values %dll_info) {
			for (@$info) {
				push(@SYMBOL_INFO, [@$_]);
			}
		}
		@SYMBOL_INFO = sort { $$a[0] <=> $$b[0] } @SYMBOL_INFO;
		open(CACHEFILE, ">$CACHEFILE");
		for $info (@SYMBOL_INFO) {
			printf CACHEFILE ("0x%08x:%s:%s\n", $$info[0], $$info[1], $$info[2]);
		}
		close(CACHEFILE);
	}
}

sub find_symbol
{
	my($address) = @_;

	my $low = 0;
	my $high = $#SYMBOL_INFO;
	my $middle;

	$address = hex($address);

	# binary search to find symbols that match provided address
	while ($low <= $high) {
		$middle = int(($low + $high) / 2);
		if ($address >= $SYMBOL_INFO[$middle][0] && ($middle >= $#SYMBOL_INFO || $address < $SYMBOL_INFO[$middle + 1][0])) {
			my @out;
			for ($m = $middle - 1; $m >= 0; --$m) {
				last if ($SYMBOL_INFO[$m][0] != $SYMBOL_INFO[$middle][0]);
				unshift(@out, sprintf("0x%08x %s %s\n", @{$SYMBOL_INFO[$m]}));
			}
			push @out, sprintf("0x%08x %s %s\n", @{$SYMBOL_INFO[$middle]});
			for ($m = $middle + 1; $m <= $#SYMBOL_INFO; ++$m) {
				last if ($SYMBOL_INFO[$m][0] != $SYMBOL_INFO[$middle][0]);
				push(@out, sprintf("0x%08x %s %s\n", @{$SYMBOL_INFO[$m]}));
			}
			print STDERR @out;
			last;
		} elsif ( $address < $SYMBOL_INFO[$middle][0] ) {
			$high = $middle - 1;
		} else {
			$low = $middle + 1;
		}
	}
}

#
# $Log: find-dll-symbol,v $
# Revision 1.1  2002/12/17 13:56:08  jhpb
# Initial revision
#

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Bug reporting:         http://cygwin.com/bugs.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/