/* symbol.c. Symbol handling routines for ksymoops. Copyright 1999 Keith Owens . Released under the GNU Public Licence, Version 2. */ #include "ksymoops.h" #include #include #include #include /* 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 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 (i < ss->used-1 && address == (s+1)->address && (s->type == 'G' || s->type == 'g')) /* more than one symbol on the same address, skip the ones that * came from modutils assist. */ continue; if (start) *start = i; return(s); } else break; } if (start) *start = i; return NULL; } /* Add a symbol to a symbol set, address in binary */ int add_symbol_n(SYMBOL_SET *ss, const 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 _R.*xxxxxxxx. */ 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); DEBUG(4, "regexec %d", i); if (i == 0) re_strings(&re_symbol_ver, symbol, re_symbol_ver_pmatch, &string); DEBUG(4, "%s %s '%c' %d '%s'", ss->source, format_address(address, NULL), type, keep, i == 0 ? string[1] : symbol); if (ss->used > ss->alloc) FATAL("ss %s used (%d) > alloc (%d)", ss->source, ss->used, ss->alloc); 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; DEBUG(4, "increasing %s from %d to %d entries", 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; s->module = NULL; /* only used in final map */ re_strings_free(&re_symbol_ver, &string); return(ss->used++); } /* Add a symbol to a symbol set, address in character */ int add_symbol(SYMBOL_SET *ss, const char *address, const char type, const char keep, const char *symbol) { addr_t a; static char const procname[] = "add_symbol"; errno = 0; a = hexstring(address); if (errno) { ERROR("address '%s' is in error", address); perror(prefix); } return(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 addr_t address, const OPTIONS *options) { int i = 0, l; SYMBOL *s; static char *map = NULL; static int size = 0; static const char procname[] = "map_address_n"; DEBUG(3, "%s %s", ss->source, format_address(address, options)); 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; if (s->module) l += strlen(s->module)+2; /* [module] */ } 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. */ addr_t offset = address - s->address; if (offset > 32768) snprintf(map, size, options->hex ? "" : "", offset); else snprintf(map, size, options->hex ? "<%s+%llx/????>" : "<%s+%lld/????>", s->name, offset); } else { snprintf(map, size, options->hex ? "<%s%s%s%s+%llx/%llx>" : "<%s%s%s%s+%lld/%lld>", s->module ? "[" : "", s->module ? s->module : "", s->module ? "]" : "", 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"; DEBUG(2, "table %s, before %d", 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) FATAL("table %s is not sorted", ss->source); } break; } } for (j = i, s = ss->symbol+j; j < ss->used; ++j, ++s) { DEBUG(4, "dropped %s", s->name); free(s->name); } ss->used = i; DEBUG(2, "table %s, after %d", ss->source, 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) { static char const procname[] = "ss_sort_atn"; DEBUG(1, "%s", 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; static char const procname[] = "ss_sort_na"; DEBUG(1, "%s", 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; static char const procname[] = "ss_copy"; DEBUG(4, "%s", 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(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); DEBUG(2, "%s %s %s", 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)) 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; static char const procname[] = "compare_Version"; 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; DEBUG(1, "Version %s", format_Version(s0->address)); for (i = 0; i < ss_Version.used; ++i, ++s) { if (s->address != s0->address) { /* format_Version uses static area, do separate calls */ WARNING_S("Version mismatch. %s says %s,", s0->name, format_Version(s0->address)); WARNING_E(" %s says %s. Expect lots of address mismatches.", s->name, format_Version(s->address)); } } prev_used = ss_Version.used; }