patch-2.2.0-pre5 linux/scripts/ksymoops/symbol.c

Next file: linux/scripts/ksymoops-0.6/Makefile
Previous file: linux/scripts/ksymoops/re.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.0-pre4/linux/scripts/ksymoops/symbol.c linux/scripts/ksymoops/symbol.c
@@ -0,0 +1,444 @@
+/*
+	symbol.c.
+
+	Symbol handling routines for ksymoops.
+
+	Copyright Keith Owens <kaos@ocs.com.au>.
+	Released under the GNU Public Licence, Version 2.
+
+	Mon Jan  4 09:08:19 EST 1999
+	Version 0.6d
+	Cast Version to int, glibc 2.1 made elf_addr_t a long.
+
+	Tue Nov  3 02:31:01 EST 1998
+	Version 0.6
+	Fix end of code calculation.
+
+	Wed Oct 28 13:47:23 EST 1998
+	Version 0.4
+	Split into separate sources.
+ */
+
+#include "ksymoops.h"
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Initialise a symbol source */
+void ss_init(SYMBOL_SET *ss, const char *msg)
+{
+	memset(ss, '\0', sizeof(*ss));
+	ss->source = strdup(msg);
+	if (!ss->source)
+		malloc_error(msg);
+}
+
+/* Free dynamic data from a symbol source */
+void ss_free(SYMBOL_SET *ss)
+{
+	int i;
+	SYMBOL *s;
+	for (s = ss->symbol, i = 0; i < ss->used; ++i, ++s)
+		free(s->name);
+	free(ss->symbol);
+	free(ss->source);
+	memset(ss, '\0', sizeof(*ss));
+}
+
+/* Initialise common symbol sets */
+void ss_init_common(void)
+{
+	ss_init(&ss_Version, "Version_");
+}
+
+/* Find a symbol name in a symbol source.  Brute force ascending order search,
+ * no hashing.  If start is not NULL, it contains the starting point for the
+ * scan and is updated to point to the found entry.  If the entry is not found,
+ * return NULL with start pointing to the next highest entry.
+ * NOTE: Assumes that ss is sorted by name.
+ */
+SYMBOL *find_symbol_name(const SYMBOL_SET *ss, const char *symbol, int *start)
+{
+	int i, l;
+	SYMBOL *s;
+	for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if ((l = strcmp(symbol, s->name)) == 0) {
+			if (start)
+				*start = i;
+			return(s);
+		}
+		if (l < 0)
+			break;
+	}
+	if (start)
+		*start = i;
+	return NULL;
+}
+
+/* Find an address in a symbol source.  Brute force ascending order search, no
+ * hashing.  If start is not NULL, it contains the starting point for the scan
+ * and is updated to point to the found entry.  If the entry is not found,
+ * return NULL with start pointing to the next highest entry.
+ * NOTE: Assumes that ss is sorted by address.
+ */
+static SYMBOL *find_symbol_address(const SYMBOL_SET *ss,
+			    const elf_addr_t address, int *start)
+{
+	int i;
+	SYMBOL *s;
+	for (i = start ? *start : 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if (address > s->address)
+			continue;
+		else if (address == s->address) {
+			if (start)
+				*start = i;
+			return(s);
+		}
+		else
+			break;
+	}
+	if (start)
+		*start = i;
+	return NULL;
+}
+
+/* Add a symbol to a symbol set, address in binary */
+void add_symbol_n(SYMBOL_SET *ss, const elf_addr_t address,
+		  const char type, const char keep, const char *symbol)
+{
+	int i;
+	char **string = NULL;
+	SYMBOL *s;
+	static regex_t     re_symbol_ver;
+	static regmatch_t *re_symbol_ver_pmatch;
+	static const char procname[] = "add_symbol_n";
+
+	/* Strip out any trailing symbol version _Rxxxxxxxx. */
+	re_compile(&re_symbol_ver,
+		"^(.*)_R[0-9a-fA-F]{8,}$",
+		REG_NEWLINE|REG_EXTENDED,
+		&re_symbol_ver_pmatch);
+
+	i = regexec(&re_symbol_ver, symbol,
+		    re_symbol_ver.re_nsub+1, re_symbol_ver_pmatch, 0);
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i);
+	if (i == 0)
+		re_strings(&re_symbol_ver, symbol, re_symbol_ver_pmatch,
+			&string);
+
+	if (debug > 3)
+		fprintf(stderr, "DEBUG: %s %s %s '%c' %d '%s'\n",
+			procname, ss->source, format_address(address),
+			type, keep, i == 0 ? string[1] : symbol);
+	if (ss->used > ss->alloc) {
+		fprintf(stderr,
+			"%s: fatal %s ss %s used (%d) > alloc (%d)\n",
+			procname, prefix, ss->source, ss->used, ss->alloc);
+		exit(2);
+	}
+	if (ss->used == ss->alloc) {
+		/* increase by 20% or 10, whichever is larger, arbitrary */
+		int newsize = ss->alloc*120/100;
+		if (newsize < ss->alloc+10)
+			newsize = ss->alloc+10;
+		if (debug > 3)
+			fprintf(stderr,
+				"DEBUG: %s increasing %s from %d to %d "
+				"entries\n",
+				procname, ss->source, ss->alloc, newsize);
+		ss->symbol = realloc(ss->symbol, newsize*sizeof(*(ss->symbol)));
+		if (!ss->symbol)
+			malloc_error("realloc ss");
+		ss->alloc = newsize;
+	}
+	s = ss->symbol+ss->used;
+	if (i == 0) {
+		s->name = string[1];
+		string[1] = NULL;	/* don't free this one */
+	}
+	else {
+		s->name = strdup(symbol);
+		if (!s->name)
+			malloc_error("strdup symbol");
+	}
+	s->type = type;
+	s->keep = keep;
+	s->address = address;
+	++ss->used;
+	re_strings_free(&re_symbol_ver, &string);
+}
+
+/* Add a symbol to a symbol set, address in character */
+void add_symbol(SYMBOL_SET *ss, const char *address, const char type,
+		const char keep, const char *symbol)
+{
+	elf_addr_t a;
+	static char const procname[] = "add_symbol";
+	errno = 0;
+	a = strtoul(address, NULL, 16);
+	if (errno) {
+		fprintf(stderr,
+			"%s: %s address '%s' is in error",
+			prefix, procname, address);
+		perror(" ");
+		++errors;
+	}
+	add_symbol_n(ss, a, type, 1, symbol);
+}
+
+/* Map an address to symbol, offset and length, address in binary */
+char *map_address(const SYMBOL_SET *ss, const elf_addr_t address)
+{
+	int i = 0, l;
+	SYMBOL *s;
+	static char *map = NULL;
+	static int size = 0;
+	static const char procname[] = "map_address_n";
+
+	if (debug > 2)
+		fprintf(stderr, "DEBUG: %s %s %s\n",
+			procname, ss->source, format_address(address));
+	s = find_symbol_address(ss, address, &i);
+	if (!s && --i >= 0)
+		s = ss->symbol+i;	/* address is between s and s+1 */
+			
+	/* Extra map text is always < 100 bytes */
+	if (s)
+		l = strlen(s->name) + 100;
+	else
+		l = 100;
+	if (l > size) {
+		map = realloc(map, l);
+		if (!map)
+			malloc_error(procname);
+		size = l;
+	}
+	if (!s) {
+		if (ss->used == 0)
+			snprintf(map, size, "No symbols available");
+		else
+			snprintf(map, size, "Before first symbol");
+	}
+	else if ((i+1) >= ss->used) {
+		/* Somewhere past last symbol.  Length of last section of code
+		 * is unknown, arbitrary cutoff at 32K.
+		 */
+		elf_addr_t offset = address - s->address;
+		if (offset > 32768)
+			snprintf(map, size, "<END_OF_CODE+%lx/????>", offset);
+		else
+			snprintf(map, size, "<%s+%lx/????>", s->name, offset);
+	}
+	else
+		snprintf(map, size,
+			"<%s+%lx/%lx>",
+			s->name, address - s->address,
+			(s+1)->address - s->address);
+	return(map);
+}
+
+/* After sorting, obsolete symbols are at the top.  Delete them. */
+static void ss_compress(SYMBOL_SET *ss)
+{
+	int i, j;
+	SYMBOL *s;
+	static const char procname[] = "ss_compress";
+
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s on table %s, before %d ",
+			procname, ss->source, ss->used);
+	for (i = 0, s = ss->symbol+i; i < ss->used; ++i, ++s) {
+		if (!s->keep) {
+			for (j = i; j < ss->used; ++j, ++s) {
+				if (s->keep) {
+					fprintf(stderr,
+						"%s: fatal %s table %s is not "
+						"sorted\n",
+						prefix, procname, ss->source);
+					exit(2);
+				}
+			}
+			break;
+		}
+	}
+	for (j = i, s = ss->symbol+j; j < ss->used; ++j, ++s)
+		free(s->name);
+	ss->used = i;
+	if (debug > 1)
+		fprintf(stderr, "after %d\n", ss->used);
+}
+
+static int ss_compare_atn(const void *a, const void *b)
+{
+	SYMBOL *c = (SYMBOL *) a;
+	SYMBOL *d = (SYMBOL *) b;
+	int i;
+
+	/* obsolete symbols to the top */
+	if (c->keep != d->keep)
+		return(d->keep - c->keep);
+	if (c->address > d->address)
+		return(1);
+	if (c->address < d->address)
+		return(-1);
+	if (c->type > d->type)
+		return(1);
+	if (c->type < d->type)
+		return(-1);
+	if ((i = strcmp(c->name, d->name)))
+		return(i);
+	return(0);
+}
+
+/* Sort a symbol set by address, type and name */
+void ss_sort_atn(SYMBOL_SET *ss)
+{
+	if (debug)
+		fprintf(stderr, "DEBUG: sorting symbols for %s (atn)\n",
+			ss->source);
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_atn);
+	ss_compress(ss);
+}
+
+static int ss_compare_na(const void *a, const void *b)
+{
+	SYMBOL *c = (SYMBOL *) a;
+	SYMBOL *d = (SYMBOL *) b;
+	int i;
+
+	/* obsolete symbols to the top */
+	if (c->keep != d->keep)
+		return(d->keep - c->keep);
+	if ((i = strcmp(c->name, d->name)))
+		return(i);
+	if (c->address > d->address)
+		return(1);
+	if (c->address < d->address)
+		return(-1);
+	return(0);
+}
+
+/* Sort a symbol set by name and address, drop duplicates.  There should be
+ * no duplicates but I have seen duplicates in ksyms on 2.0.35.
+ */
+void ss_sort_na(SYMBOL_SET *ss)
+{
+	int i;
+	SYMBOL *s;
+	if (debug)
+		fprintf(stderr, "DEBUG: sorting symbols for %s (na)\n",
+			ss->source);
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_na);
+	ss_compress(ss);
+	s = ss->symbol;
+	for (i = 0; i < ss->used-1; ++i) {
+		if (strcmp(s->name, (s+1)->name) == 0 &&
+		    s->address == (s+1)->address) {
+			if (s->type != ' ')
+				(s+1)->keep = 0;
+			else
+				s->keep = 0;
+		}
+		++s;
+	}
+	qsort((char *) ss->symbol, (unsigned) ss->used,
+		sizeof(*(ss->symbol)), ss_compare_na);
+	ss_compress(ss);
+}
+
+/* Copy a symbol set, including all its strings */
+SYMBOL_SET *ss_copy(const SYMBOL_SET *ss)
+{
+	SYMBOL_SET *ssc;
+	if (debug > 3)
+		fprintf(stderr,
+			"DEBUG: ss_copy %s\n", ss->source);
+	ssc = malloc(sizeof(*ssc));
+	if (!ssc)
+		malloc_error("copy ssc");
+	ss_init(ssc, ss->source);
+	ssc->used = ss->used;
+	ssc->alloc = ss->used;	/* shrink the copy */
+	ssc->symbol = malloc(ssc->used*sizeof(*(ssc->symbol)));
+	if (!(ssc->symbol))
+		malloc_error("copy ssc symbols");
+	memcpy(ssc->symbol, ss->symbol, ssc->used*sizeof(*(ssc->symbol)));
+	return(ssc);
+}
+
+/* Convert version number to major, minor string.  */
+static const char *format_Version(elf_addr_t Version)
+{
+	static char string[12];	/* 255.255.255\0 worst case */
+	snprintf(string, sizeof(string), "%d.%d.%d",
+		(int) ((Version >> 16) & 0xff),
+		(int) ((Version >> 8) & 0xff),
+		(int) ((Version) & 0xff));
+	return(string);
+}
+
+/* Save version number.  The "address" is the version number, the "symbol" is
+ * the source of the version.
+ */
+void add_Version(const char *version, const char *source)
+{
+	static char const procname[] = "add_Version";
+	int i = atoi(version);
+	if (debug > 1)
+		fprintf(stderr, "DEBUG: %s %s %s %s\n",
+			procname, source, version, format_Version(i));
+	add_symbol_n(&ss_Version, i, 'V', 1, source);
+}
+
+/* Extract Version_ number from a symbol set and save it.  */
+void extract_Version(SYMBOL_SET *ss)
+{
+	int i = 0;
+	SYMBOL *s;
+
+	s = find_symbol_name(ss, "Version_", &i);
+	if (!s && i < ss->used)
+		s = ss->symbol+i;	/* first symbol after "Version_" */
+	if (!s || strncmp(s->name, "Version_", 8))
+		return;
+	add_Version(s->name+8, ss->source);
+}
+
+/* Compare all extracted Version numbers.  Silent unless there is a problem. */
+void compare_Version(void)
+{
+	int i = 0;
+	SYMBOL *s, *s0;
+	static int prev_used = 0;
+
+	if (!ss_Version.used)
+		return;
+	/* Only check if the Version table has changed in size */
+	if (prev_used == ss_Version.used)
+		return;
+
+	ss_sort_na(&ss_Version);
+	s0 = s = ss_Version.symbol;
+	if (debug)
+		fprintf(stderr, "DEBUG: Version %s\n",
+			format_Version(s0->address));
+	for (i = 0; i < ss_Version.used; ++i, ++s) {
+		if (s->address != s0->address) {
+			fprintf(stderr,
+				"Version mismatch error.  %s says %s, ",
+				s0->name,
+				format_Version(s0->address));
+			fprintf(stderr,
+				"%s says %s.  Expect lots of address "
+				"mismatches.\n",
+				s->name,
+				format_Version(s->address));
+			++errors;
+		}
+	}
+	prev_used = ss_Version.used;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov