diff options
Diffstat (limited to 'scripts/ksymoops/symbol.c')
-rw-r--r-- | scripts/ksymoops/symbol.c | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/scripts/ksymoops/symbol.c b/scripts/ksymoops/symbol.c new file mode 100644 index 000000000..5c66cc994 --- /dev/null +++ b/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; +} |