diff options
Diffstat (limited to 'scripts/ksymoops/map.c')
-rw-r--r-- | scripts/ksymoops/map.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/scripts/ksymoops/map.c b/scripts/ksymoops/map.c new file mode 100644 index 000000000..6f91e9daf --- /dev/null +++ b/scripts/ksymoops/map.c @@ -0,0 +1,251 @@ +/* + map.c. + + Read System.map for ksymoops, create merged System.map. + + Copyright Keith Owens <kaos@ocs.com.au>. + Released under the GNU Public Licence, Version 2. + + Tue Nov 3 02:31:01 EST 1998 + Version 0.6 + Remove addresses 0-4095 from merged map after writing new map. + Move "Using_Version" copy to map.c. + + Wed Oct 28 13:47:23 EST 1998 + Version 0.4 + Split into separate sources. + */ + +#include "ksymoops.h" +#include <malloc.h> + +/* Read the symbols from System.map */ +void read_system_map(const char *system_map) +{ + FILE *f; + char *line = NULL, **string = NULL; + int i, size = 0; + static char const procname[] = "read_system_map"; + + if (!system_map) + return; + ss_init(&ss_system_map, "System.map"); + if (debug) + fprintf(stderr, "DEBUG: %s %s\n", procname, system_map); + + if (!regular_file(system_map, procname)) + return; + + if (!(f = fopen_local(system_map, "r", procname))) + return; + + while (fgets_local(&line, &size, f, procname)) { + i = regexec(&re_nm, line, re_nm.re_nsub+1, re_nm_pmatch, 0); + if (debug > 3) + fprintf(stderr, "DEBUG: %s regexec %d\n", procname, i); + if (i == 0) { + re_strings(&re_nm, line, re_nm_pmatch, &string); + add_symbol(&ss_system_map, string[1], *string[2], + 1, string[3]); + } + } + + fclose_local(f, procname); + re_strings_free(&re_nm, &string); + free(line); + if (ss_system_map.used) { + ss_sort_na(&ss_system_map); + extract_Version(&ss_system_map); + } + else { + fprintf(stderr, + "Warning, no kernel symbols in System.map, is %s a " + "valid System.map file?\n", + system_map); + ++warnings; + } + + if (debug > 1) + fprintf(stderr, + "DEBUG: %s %s used %d out of %d entries\n", + procname, + ss_system_map.source, + ss_system_map.used, + ss_system_map.alloc); +} + +/* Compare two maps, all symbols in the first should appear in the second. */ +void compare_maps(const SYMBOL_SET *ss1, const SYMBOL_SET *ss2, + int precedence) +{ + int i, start = 0; + SYMBOL *s1, *s2, **sdrop = precedence == 1 ? &s2 : &s1; + const SYMBOL_SET **ssdrop = precedence == 1 ? &ss2 : &ss1; + + if (!(ss1->used && ss2->used)) + return; + + if (debug > 1) + fprintf(stderr, + "DEBUG: compare_maps %s vs %s, %s takes precedence\n", + ss1->source, ss2->source, + precedence == 1 ? ss1->source : ss2->source); + + for (i = 0; i < ss1->used; ++i) { + s1 = ss1->symbol+i; + if (!(s1->keep)) + continue; + s2 = find_symbol_name(ss2, s1->name, &start); + if (!s2) { + /* Some types only appear in nm output, not in things + * like System.map. Silently ignore them. + */ + if (s1->type == 'a' || s1->type == 't') + continue; + fprintf(stderr, + "Warning: %s symbol %s not found in %s. " + "Ignoring %s entry\n", + ss1->source, s1->name, + ss2->source, (*ssdrop)->source); + ++warnings; + if (*sdrop) + (*sdrop)->keep = 0; + } + else if (s1->address != s2->address) { + /* Type C symbols cannot be resolved from nm to ksyms, + * silently ignore them. + */ + if (s1->type == 'C' || s2->type == 'C') + continue; + fprintf(stderr, + "Warning: mismatch on symbol %s %c, " + "%s says %lx, %s says %lx. " + "Ignoring %s entry\n", + s1->name, s1->type, ss1->source, s1->address, + ss2->source, s2->address, (*ssdrop)->source); + ++warnings; + if (*sdrop) + (*sdrop)->keep = 0; + } + else + ++start; /* step to next entry in ss2 */ + } +} + +/* Append the second symbol set onto the first */ +static void append_map(SYMBOL_SET *ss1, const SYMBOL_SET *ss2) +{ + int i; + SYMBOL *s; + + if (!ss2 || !ss2->used) + return; + if (debug > 1) + fprintf(stderr, "DEBUG: append_map %s to %s\n", + ss2->source, ss1->source); + + for (i = 0; i < ss2->used; ++i) { + s = ss2->symbol+i; + if (s->keep) + add_symbol_n(ss1, s->address, s->type, 1, + s->name); + } +} + +/* Compare the various sources and build a merged system map */ +void merge_maps(const char *save_system_map) +{ + int i; + SYMBOL *s; + FILE *f; + static char const procname[] = "merge_maps"; + + if (debug) + fprintf(stderr, "DEBUG: %s\n", procname); + + /* Using_Versions only appears in ksyms, copy to other tables */ + if ((s = find_symbol_name(&ss_ksyms_base, + "Using_Versions", 0))) { + if (ss_system_map.used) { + add_symbol_n(&ss_system_map, s->address, + s->type, s->keep, s->name); + ss_sort_na(&ss_system_map); + } + if (ss_vmlinux.used) { + add_symbol_n(&ss_vmlinux, s->address, s->type, + s->keep, s->name); + ss_sort_na(&ss_vmlinux); + } + } + + compare_Version(); /* highlight any version problems first */ + compare_ksyms_lsmod(); /* highlight any missing modules next */ + compare_maps(&ss_ksyms_base, &ss_vmlinux, 2); + compare_maps(&ss_system_map, &ss_vmlinux, 2); + compare_maps(&ss_vmlinux, &ss_system_map, 1); + compare_maps(&ss_ksyms_base, &ss_system_map, 2); + + if (ss_objects) { + map_ksyms_to_modules(); + } + + ss_init(&ss_merged, "merged"); + append_map(&ss_merged, &ss_vmlinux); + append_map(&ss_merged, &ss_ksyms_base); + append_map(&ss_merged, &ss_system_map); + for (i = 0; i < ss_ksyms_modules; ++i) + append_map(&ss_merged, (ss_ksyms_module+i)->related); + if (!ss_merged.used) { + fprintf(stderr, "Warning, no symbols in merged map\n"); + ++warnings; + } + + /* drop duplicates, type a (registers) and gcc2_compiled. */ + ss_sort_atn(&ss_merged); + s = ss_merged.symbol; + for (i = 0; i < ss_merged.used-1; ++i) { + if (s->type == 'a' || + (s->type == 't' && !strcmp(s->name, "gcc2_compiled."))) + s->keep = 0; + else 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; + } + ss_sort_atn(&ss_merged); /* will remove dropped variables */ + + if (save_system_map) { + if (debug) + fprintf(stderr, "DEBUG: writing merged map to %s\n", + save_system_map); + if (!(f = fopen_local(save_system_map, "w", procname))) + return; + s = ss_merged.symbol; + for (i = 0; i < ss_merged.used; ++i) { + if (s->keep) + fprintf(f, "%s %c %s\n", + format_address(s->address), + s->type, s->name); + ++s; + } + } + + /* The merged map may contain symbols with an address of 0, e.g. + * Using_Versions. These give incorrect results for low addresses in + * map_address, such addresses map to "Using_Versions+xxx". Remove + * any addresses below (arbitrary) 4096 from the merged map. AFAIK, + * Linux does not use the first page on any arch. + */ + for (i = 0; i < ss_merged.used; ++i) { + if ((ss_merged.symbol+i)->address < 4096) + (ss_merged.symbol+i)->keep = 0; + else + break; + } + if (i) + ss_sort_atn(&ss_merged); /* remove dropped variables */ +} |