--- crash-4.0-4.4/main.c 2007-07-27 15:15:47.000000000 -0400 +++ crash-4.0-4.5/main.c 2007-07-26 14:11:36.000000000 -0400 @@ -54,6 +54,7 @@ {"less", 0, 0, 0}, {"CRASHPAGER", 0, 0, 0}, {"no_scroll", 0, 0, 0}, + {"reloc", required_argument, 0, 0}, {0, 0, 0, 0} }; @@ -183,6 +184,15 @@ else if (STREQ(long_options[option_index].name, "no_crashrc")) pc->flags |= NOCRASHRC; + else if (STREQ(long_options[option_index].name, "reloc")) { + if (!calculate(optarg, &kt->relocate, NULL, 0)) { + error(INFO, "invalid --reloc argument: %s\n", + optarg); + program_usage(SHORT_FORM); + } + kt->flags |= RELOC_SET; + } + else { error(INFO, "internal error: option %s unhandled\n", long_options[option_index].name); --- crash-4.0-4.4/tools.c 2007-07-27 15:15:47.000000000 -0400 +++ crash-4.0-4.5/tools.c 2007-07-26 14:11:50.000000000 -0400 @@ -18,7 +18,6 @@ #include "defs.h" #include -static int calculate(char *, ulong *, ulonglong *, ulong); static void print_number(struct number_option *, int, int); static long alloc_hq_entry(void); struct hq_entry; @@ -2660,7 +2659,7 @@ * its real value. The allowable multipliers are k, K, m, M, g and G, for * kilobytes, megabytes and gigabytes. */ -static int +int calculate(char *s, ulong *value, ulonglong *llvalue, ulong flags) { ulong factor, bias; --- crash-4.0-4.4/memory.c 2007-07-27 15:15:47.000000000 -0400 +++ crash-4.0-4.5/memory.c 2007-07-27 12:03:17.000000000 -0400 @@ -408,6 +408,9 @@ MEMBER_OFFSET_INIT(kmem_list3_free_objects, "kmem_list3", "free_objects"); MEMBER_OFFSET_INIT(kmem_list3_shared, "kmem_list3", "shared"); + } else if (MEMBER_EXISTS("kmem_cache", "cpu_slab") && + STRUCT_EXISTS("kmem_cache_node")) { + vt->flags |= KMALLOC_SLUB; } else { MEMBER_OFFSET_INIT(kmem_cache_s_c_nextp, "kmem_cache_s", "c_nextp"); @@ -706,6 +709,8 @@ vt->dump_kmem_cache = dump_kmem_cache_percpu_v1; else if (vt->flags & PERCPU_KMALLOC_V2) vt->dump_kmem_cache = dump_kmem_cache_percpu_v2; + else if (vt->flags & KMALLOC_SLUB) + vt->flags |= KMEM_CACHE_UNAVAIL; /* TBD */ else vt->dump_kmem_cache = dump_kmem_cache; @@ -10286,6 +10291,8 @@ fprintf(fp, "%sPERCPU_KMALLOC_V2_NODES", others++ ? "|" : "");\ if (vt->flags & VM_STAT) fprintf(fp, "%sVM_STAT", others++ ? "|" : "");\ + if (vt->flags & KMALLOC_SLUB) + fprintf(fp, "%sKMALLOC_SLUB", others++ ? "|" : "");\ fprintf(fp, ")\n"); if (vt->kernel_pgd[0] == vt->kernel_pgd[1]) @@ -12257,8 +12264,12 @@ if (!symbol_exists("node_online_map")) return 0; - len = get_symbol_type("node_online_map", NULL, &req) == TYPE_CODE_UNDEF ? - sizeof(ulong) : req.length; + if (LKCD_KERNTYPES()) { + if ((len = STRUCT_SIZE("nodemask_t")) < 0) + error(FATAL, "cannot determine type nodemask_t\n"); + } else + len = get_symbol_type("node_online_map", NULL, &req) + == TYPE_CODE_UNDEF ? sizeof(ulong) : req.length; if (!(vt->node_online_map = (ulong *)malloc(len))) error(FATAL, "cannot malloc node_online_map\n"); @@ -12327,27 +12338,36 @@ /* * Default -- look for type: struct pglist_data node_data[] */ - if (get_symbol_type("node_data", NULL, NULL) != TYPE_CODE_ARRAY) - goto pgdat2; + if (LKCD_KERNTYPES()) { + if (!kernel_symbol_exists("node_data")) + goto pgdat2; + /* + * Just index into node_data[] without checking that it is + * an array; kerntypes have no such symbol information. + */ + } else { + if (get_symbol_type("node_data", NULL, NULL) != TYPE_CODE_ARRAY) + goto pgdat2; - open_tmpfile(); - sprintf(buf, "whatis node_data"); - if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { - close_tmpfile(); - goto pgdat2; - } - rewind(pc->tmpfile); - while (fgets(buf, BUFSIZE, pc->tmpfile)) { - if (STRNEQ(buf, "type = ")) - break; - } - close_tmpfile(); + open_tmpfile(); + sprintf(buf, "whatis node_data"); + if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { + close_tmpfile(); + goto pgdat2; + } + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (STRNEQ(buf, "type = ")) + break; + } + close_tmpfile(); - if ((!strstr(buf, "struct pglist_data *") && - !strstr(buf, "pg_data_t *")) || - (count_chars(buf, '[') != 1) || - (count_chars(buf, ']') != 1)) - goto pgdat2; + if ((!strstr(buf, "struct pglist_data *") && + !strstr(buf, "pg_data_t *")) || + (count_chars(buf, '[') != 1) || + (count_chars(buf, ']') != 1)) + goto pgdat2; + } if (!readmem(symbol_value("node_data") + (node * sizeof(void *)), KVADDR, &pgdat, sizeof(void *), "node_data", RETURN_ON_ERROR) || @@ -12357,27 +12377,32 @@ return pgdat; pgdat2: - if (get_symbol_type("pgdat_list", NULL, NULL) != TYPE_CODE_ARRAY) - goto pgdat3; + if (LKCD_KERNTYPES()) { + if (!kernel_symbol_exists("pgdat_list")) + goto pgdat3; + } else { + if (get_symbol_type("pgdat_list",NULL,NULL) != TYPE_CODE_ARRAY) + goto pgdat3; - open_tmpfile(); - sprintf(buf, "whatis pgdat_list"); - if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { - close_tmpfile(); - goto pgdat3; - } - rewind(pc->tmpfile); - while (fgets(buf, BUFSIZE, pc->tmpfile)) { - if (STRNEQ(buf, "type = ")) - break; - } - close_tmpfile(); + open_tmpfile(); + sprintf(buf, "whatis pgdat_list"); + if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { + close_tmpfile(); + goto pgdat3; + } + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (STRNEQ(buf, "type = ")) + break; + } + close_tmpfile(); - if ((!strstr(buf, "struct pglist_data *") && - !strstr(buf, "pg_data_t *")) || - (count_chars(buf, '[') != 1) || - (count_chars(buf, ']') != 1)) - goto pgdat3; + if ((!strstr(buf, "struct pglist_data *") && + !strstr(buf, "pg_data_t *")) || + (count_chars(buf, '[') != 1) || + (count_chars(buf, ']') != 1)) + goto pgdat3; + } if (!readmem(symbol_value("pgdat_list") + (node * sizeof(void *)), KVADDR, &pgdat, sizeof(void *), "pgdat_list", RETURN_ON_ERROR) || @@ -12414,27 +12439,36 @@ /* * look for type: type = atomic_long_t [] */ - if (!symbol_exists("vm_stat") || - get_symbol_type("vm_stat", NULL, NULL) != TYPE_CODE_ARRAY) - goto bailout; - - open_tmpfile(); - sprintf(buf, "whatis vm_stat"); - if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { - close_tmpfile(); - goto bailout; - } - rewind(pc->tmpfile); - while (fgets(buf, BUFSIZE, pc->tmpfile)) { - if (STRNEQ(buf, "type = ")) - break; - } - close_tmpfile(); + if (LKCD_KERNTYPES()) { + if (!symbol_exists("vm_stat")) + goto bailout; + /* + * Just assume that vm_stat is an array; there is + * no symbol info in a kerntypes file. + */ + } else { + if (!symbol_exists("vm_stat") || + get_symbol_type("vm_stat", NULL, NULL) != TYPE_CODE_ARRAY) + goto bailout; + + open_tmpfile(); + sprintf(buf, "whatis vm_stat"); + if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { + close_tmpfile(); + goto bailout; + } + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (STRNEQ(buf, "type = ")) + break; + } + close_tmpfile(); - if (!strstr(buf, "atomic_long_t") || - (count_chars(buf, '[') != 1) || - (count_chars(buf, ']') != 1)) - goto bailout; + if (!strstr(buf, "atomic_long_t") || + (count_chars(buf, '[') != 1) || + (count_chars(buf, ']') != 1)) + goto bailout; + } open_tmpfile(); req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); --- crash-4.0-4.4/kernel.c 2007-07-27 15:15:47.000000000 -0400 +++ crash-4.0-4.5/kernel.c 2007-07-27 12:03:17.000000000 -0400 @@ -170,7 +170,7 @@ else i = get_array_length("__per_cpu_offset", NULL, 0); get_symbol_data("__per_cpu_offset", - sizeof(long)*(i <= NR_CPUS ? i : NR_CPUS), + sizeof(long)*((i && (i <= NR_CPUS)) ? i : NR_CPUS), &kt->__per_cpu_offset[0]); kt->flags |= PER_CPU_OFF; } @@ -3680,7 +3680,7 @@ others = 0; uts = &kt->utsname; - fprintf(fp, " flags: %lx (", kt->flags); + fprintf(fp, " flags: %lx\n (", kt->flags); if (kt->flags & NO_MODULE_ACCESS) fprintf(fp, "%sNO_MODULE_ACCESS", others++ ? "|" : ""); if (kt->flags & TVEC_BASES_V1) @@ -3733,6 +3733,10 @@ fprintf(fp, "%sDWARF_UNWIND_MODULES", others++ ? "|" : ""); if (kt->flags & BUGVERBOSE_OFF) fprintf(fp, "%sBUGVERBOSE_OFF", others++ ? "|" : ""); + if (kt->flags & RELOC_SET) + fprintf(fp, "%sRELOC_SET", others++ ? "|" : ""); + if (kt->flags & RELOC_FORCE) + fprintf(fp, "%sRELOC_FORCE", others++ ? "|" : ""); fprintf(fp, ")\n"); fprintf(fp, " stext: %lx\n", kt->stext); fprintf(fp, " etext: %lx\n", kt->etext); @@ -3774,6 +3778,7 @@ fprintf(fp, " gcc_version: %d.%d.%d\n", kt->gcc_version[0], kt->gcc_version[1], kt->gcc_version[2]); fprintf(fp, " BUG_bytes: %d\n", kt->BUG_bytes); + fprintf(fp, " relocate: %lx\n", kt->relocate); fprintf(fp, " runq_siblings: %d\n", kt->runq_siblings); fprintf(fp, " __rq_idx[NR_CPUS]: "); nr_cpus = kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS; @@ -5494,8 +5499,12 @@ if (!symbol_exists("cpu_online_map")) return 0; - len = get_symbol_type("cpu_online_map", NULL, &req) == TYPE_CODE_UNDEF ? - sizeof(ulong) : req.length; + if (LKCD_KERNTYPES()) { + if ((len = STRUCT_SIZE("cpumask_t")) < 0) + error(FATAL, "cannot determine type cpumask_t\n"); + } else + len = get_symbol_type("cpu_online_map", NULL, &req) == + TYPE_CODE_UNDEF ? sizeof(ulong) : req.length; buf = GETBUF(len); online = 0; @@ -5530,8 +5539,12 @@ if (!symbol_exists("cpu_possible_map")) return 0; - len = get_symbol_type("cpu_possible_map", NULL, &req) == - TYPE_CODE_UNDEF ? sizeof(ulong) : req.length; + if (LKCD_KERNTYPES()) { + if ((len = STRUCT_SIZE("cpumask_t")) < 0) + error(FATAL, "cannot determine type cpumask_t\n"); + } else + len = get_symbol_type("cpu_possible_map", NULL, &req) == + TYPE_CODE_UNDEF ? sizeof(ulong) : req.length; buf = GETBUF(len); possible = 0; --- crash-4.0-4.4/gdb_interface.c 2007-07-27 15:15:47.000000000 -0400 +++ crash-4.0-4.5/gdb_interface.c 2007-07-26 14:13:09.000000000 -0400 @@ -251,7 +251,7 @@ * Patch gdb's symbol values with the correct values from either * the System.map or non-debug vmlinux, whichever is in effect. */ - if ((pc->flags & SYSMAP) || + if ((pc->flags & SYSMAP) || (kt->flags & (RELOC_SET|RELOC_FORCE)) || (pc->namelist_debug && !pc->debuginfo_file)) { req->command = GNU_PATCH_SYMBOL_VALUES; req->flags = GNU_RETURN_ON_ERROR; --- crash-4.0-4.4/symbols.c 2007-07-27 15:15:47.000000000 -0400 +++ crash-4.0-4.5/symbols.c 2007-07-26 14:11:05.000000000 -0400 @@ -21,6 +21,8 @@ static void store_symbols(bfd *, int, void *, long, unsigned int); static void store_sysmap_symbols(void); +static ulong relocate(ulong, char *, int); +static int relocate_force(ulong, char *); static void strip_module_symbol_end(char *s); static int compare_syms(const void *, const void *); static int compare_mods(const void *, const void *); @@ -487,6 +489,11 @@ kt->stext_init = (ulong)bfd_get_section_vma(st->bfd, section); kt->etext_init = kt->stext_init + (ulong)bfd_section_size(st->bfd, section); + + if (kt->relocate) { + kt->stext_init -= kt->relocate; + kt->etext_init -= kt->relocate; + } } /* @@ -502,6 +509,7 @@ bfd_byte *from, *fromend; symbol_info syminfo; struct syment *sp; + int first; if ((store = bfd_make_empty_symbol(abfd)) == NULL) error(FATAL, "bfd_make_empty_symbol() failed\n"); @@ -521,6 +529,13 @@ st->symcnt = 0; sp = st->symtable; + if (machine_type("X86")) { + if (!(kt->flags & RELOC_SET)) + kt->flags |= RELOC_FORCE; + } else + kt->flags &= ~RELOC_SET; + + first = 0; from = (bfd_byte *) minisyms; fromend = from + symcount * size; for (; from < fromend; from += size) @@ -532,7 +547,11 @@ bfd_get_symbol_info(abfd, sym, &syminfo); if (machdep->verify_symbol(syminfo.name, syminfo.value, syminfo.type)) { - sp->value = syminfo.value; + if (kt->flags & (RELOC_SET|RELOC_FORCE)) + sp->value = relocate(syminfo.value, + (char *)syminfo.name, !(first++)); + else + sp->value = syminfo.value; sp->type = syminfo.type; namespace_ctl(NAMESPACE_INSTALL, &st->namespace, sp, (char *)syminfo.name); @@ -556,7 +575,7 @@ static void store_sysmap_symbols(void) { - int c; + int c, first; long symcount; char buf[BUFSIZE]; FILE *map; @@ -580,6 +599,10 @@ error(FATAL, "symbol table namespace malloc: %s\n", strerror(errno)); + if (!machine_type("X86")) + kt->flags &= ~RELOC_SET; + + first = 0; st->syment_size = symcount * sizeof(struct syment); st->symcnt = 0; sp = st->symtable; @@ -596,7 +619,11 @@ if (machdep->verify_symbol(syment.name, syment.value, syment.type)) { - sp->value = syment.value; + if (kt->flags & RELOC_SET) + sp->value = relocate(syment.value, + syment.name, !(first++)); + else + sp->value = syment.value; sp->type = syment.type; namespace_ctl(NAMESPACE_INSTALL, &st->namespace, sp, syment.name); @@ -619,6 +646,96 @@ } /* + * Handle x86 kernels configured such that the vmlinux symbols + * are not as loaded into the kernel (not unity-mapped). + */ +static ulong +relocate(ulong symval, char *symname, int first_symbol) +{ + switch (kt->flags & (RELOC_SET|RELOC_FORCE)) + { + case RELOC_SET: + break; + + case RELOC_FORCE: + if (first_symbol && !relocate_force(symval, symname)) + kt->flags &= ~RELOC_FORCE; + break; + } + + return (symval - kt->relocate); +} + +/* + * If no --reloc argument was passed, try to figure it out + * by comparing the first vmlinux kernel symbol with the + * first /proc/kallsyms symbol. (should be "_text") + * + * Live system only (at least for now). + */ +static int +relocate_force(ulong symval, char *symname) +{ + FILE *kp; + char buf[BUFSIZE]; + char *kallsyms[MAXARGS]; + ulong first; + + if (!ACTIVE() || !file_exists("/proc/kallsyms", NULL)) { + if (CRASHDEBUG(1)) + fprintf(fp, + "cannot determine relocation value: %s\n", + !ACTIVE() ? "not a live system" : + "/proc/kallsyms does not exist"); + return FALSE; + } + + if ((kp = fopen("/proc/kallsyms", "r")) == NULL) { + if (CRASHDEBUG(1)) + fprintf(fp, + "cannot open /proc/kallsyms to determine relocation\n"); + return FALSE; + } + + if (!fgets(buf, BUFSIZE, kp) || + (parse_line(buf, kallsyms) != 3) || + !hexadecimal(kallsyms[0], 0)) { + fclose(kp); + if (CRASHDEBUG(1)) + fprintf(fp, + "malformed /proc/kallsyms: cannot determine relocation value\n"); + return FALSE; + } + fclose(kp); + + first = htol(kallsyms[0], RETURN_ON_ERROR, NULL); + + if (CRASHDEBUG(1)) + fprintf(fp, + "RELOCATE: %s @ %lx %s\n" + " %s @ %lx /proc/kallsyms\n", + symname, symval, pc->namelist, + kallsyms[2], first); + + /* + * If the symbols match and have different values, + * force the relocation. + */ + if (STREQ(symname, kallsyms[2])) { + if (symval > first) { + kt->relocate = symval - first; + return TRUE; + } + } + + if (CRASHDEBUG(1)) + fprintf(fp, + "cannot determine relocation value from first symbol\n"); + + return FALSE; +} + +/* * Install all static kernel symbol values into the symval_hash. */ static void @@ -8433,6 +8550,10 @@ struct syment *sp_array[200], *sp; if (req->name == PATCH_KERNEL_SYMBOLS_START) { + if (kt->flags & RELOC_FORCE) + error(WARNING, + "\nkernel relocated [%ldMB]: patching %ld gdb minimal_symbol values\n", + kt->relocate >> 20, st->symcnt); fprintf(fp, (pc->flags & SILENT) || !(pc->flags & TTY) ? "" : "\nplease wait... (patching %ld gdb minimal_symbol values) ", st->symcnt); --- crash-4.0-4.4/defs.h 2007-07-27 15:15:47.000000000 -0400 +++ crash-4.0-4.5/defs.h 2007-07-27 15:14:28.000000000 -0400 @@ -461,6 +461,8 @@ #define DWARF_UNWIND_CAPABLE (DWARF_UNWIND_MEMORY|DWARF_UNWIND_EH_FRAME) #define DWARF_UNWIND_MODULES (0x800000) #define BUGVERBOSE_OFF (0x1000000) +#define RELOC_SET (0x2000000) +#define RELOC_FORCE (0x4000000) #define GCC_VERSION_DEPRECATED (GCC_3_2|GCC_3_2_3|GCC_2_96|GCC_3_3_2|GCC_3_3_3) @@ -525,6 +527,7 @@ ulong p2m_pages_searched; ulong p2m_mfn_cache_hits; ulong p2m_page_cache_hits; + ulong relocate; }; /* @@ -1650,6 +1653,7 @@ #define KMEM_CACHE_DELAY (0x2000) #define NODES_ONLINE (0x4000) #define VM_STAT (0x8000) +#define KMALLOC_SLUB (0x10000) #define IS_FLATMEM() (vt->flags & FLATMEM) #define IS_DISCONTIGMEM() (vt->flags & DISCONTIGMEM) @@ -3134,6 +3138,7 @@ void please_wait(char *); void please_wait_done(void); int pathcmp(char *, char *); +int calculate(char *, ulong *, ulonglong *, ulong); /*