/* * Used by gdb_interface() to catch gdb-related errors, if desired. */ void gdb_error_hook(void) { char buf1[BUFSIZE]; char buf2[BUFSIZE]; int buffers; if (CRASHDEBUG(2)) { sprintf(buf2, "\n"); if (CRASHDEBUG(5) && (buffers = get_embedded())) sprintf(buf2, "(%d buffer%s in use)\n", buffers, buffers > 1 ? "s" : ""); fprintf(stderr, "%s: returned via gdb_error_hook %s", gdb_command_string(pc->cur_gdb_cmd, buf1, TRUE), buf2); console("%s: returned via gdb_error_hook %s", gdb_command_string(pc->cur_gdb_cmd, buf1, TRUE), buf2); } #ifdef GDB_7_6 do_cleanups(all_cleanups()); #else do_cleanups(NULL); #endif longjmp(pc->gdb_interface_env, 1); }
int read_ramdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) { off_t offset; int i, found; struct ramdump_def *r = &ramdump[0]; offset = 0; for (i = found = 0; i < nodes; i++) { r = &ramdump[i]; if ((paddr >= r->start_paddr) && (paddr <= r->end_paddr)) { offset = (off_t)paddr - (off_t)r->start_paddr; found++; break; } } if (!found) { if (CRASHDEBUG(8)) fprintf(fp, "read_ramdump: READ_ERROR: " "offset not found for paddr: %llx\n", (ulonglong)paddr); return READ_ERROR; } if (CRASHDEBUG(8)) fprintf(fp, "read_ramdump: addr: %lx paddr: %llx cnt: %d offset: %llx\n", addr, (ulonglong)paddr, cnt, (ulonglong)offset); if (lseek(r->rfd, offset, SEEK_SET) == -1) { if (CRASHDEBUG(8)) fprintf(fp, "read_ramdump: SEEK_ERROR: " "offset: %llx\n", (ulonglong)offset); return SEEK_ERROR; } if (read(r->rfd, bufptr, cnt) != cnt) { if (CRASHDEBUG(8)) fprintf(fp, "read_ramdump: READ_ERROR: " "offset: %llx\n", (ulonglong)offset); return READ_ERROR; } return cnt; }
static int read_raw_dump_file(int fd, off_t offset, void *buf, size_t size) { if (lseek(fd, offset, SEEK_SET) < 0) { if (CRASHDEBUG(1)) error(INFO, "cannot lseek dump file\n"); return FALSE; } if (read(fd, buf, size) < size) { if (CRASHDEBUG(1)) error(INFO, "cannot read dump file\n"); return FALSE; } return TRUE; }
/* * Determine whether a file is a diskdump creation, and if TRUE, * initialize the diskdump_data structure based upon the contents * of the diskdump header data. */ int is_diskdump(char *file) { int sz, i; if (!open_dump_file(file) || !read_dump_header(file)) return FALSE; sz = dd->block_size * (DISKDUMP_CACHED_PAGES); if ((dd->page_cache_buf = malloc(sz)) == NULL) error(FATAL, "%s: cannot malloc compressed page_cache_buf\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); for (i = 0; i < DISKDUMP_CACHED_PAGES; i++) dd->page_cache_hdr[i].pg_bufptr = &dd->page_cache_buf[i * dd->block_size]; if ((dd->compressed_page = (char *)malloc(dd->block_size)) == NULL) error(FATAL, "%s: cannot malloc compressed page space\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); if (CRASHDEBUG(1)) __diskdump_memory_dump(fp); if (pc->flags2 & GET_OSRELEASE) diskdump_get_osrelease(); #ifdef LZO if (lzo_init() == LZO_E_OK) dd->flags |= LZO_SUPPORTED; #endif return TRUE; }
void map_cpus_to_prstatus_kdump_cmprs(void) { void **nt_ptr; int online, i, j, nrcpus; size_t size; if (!(online = get_cpus_online()) || (online == kt->cpus)) return; if (CRASHDEBUG(1)) error(INFO, "cpus: %d online: %d NT_PRSTATUS notes: %d (remapping)\n", kt->cpus, online, dd->num_prstatus_notes); size = NR_CPUS * sizeof(void *); nt_ptr = (void **)GETBUF(size); BCOPY(dd->nt_prstatus_percpu, nt_ptr, size); BZERO(dd->nt_prstatus_percpu, size); /* * Re-populate the array with the notes mapping to online cpus */ nrcpus = (kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS); for (i = 0, j = 0; i < nrcpus; i++) { if (in_cpu_map(ONLINE, i)) dd->nt_prstatus_percpu[i] = nt_ptr[j++]; } FREEBUF(nt_ptr); }
/* * Quickest way to gdb -- just pass a command string to pass through. */ int gdb_pass_through(char *cmd, FILE *fptr, ulong flags) { struct gnu_request *req; int retval; if (CRASHDEBUG(1)) console("gdb_pass_through: [%s]\n", cmd); req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); req->buf = cmd; if (fptr) req->fp = fptr; req->command = GNU_PASS_THROUGH; req->flags = flags; gdb_interface(req); if ((req->flags & (GNU_RETURN_ON_ERROR|GNU_COMMAND_FAILED)) == (GNU_RETURN_ON_ERROR|GNU_COMMAND_FAILED)) retval = FALSE; else retval = TRUE; FREEBUF(req); return retval; }
ulong get_diskdump_panic_task(void) { int i; if ((!DISKDUMP_VALID() && !KDUMP_CMPRS_VALID()) || !get_active_set()) return NO_TASK; if (DISKDUMP_VALID()) return (ulong)dd->header->tasks[dd->header->current_cpu]; if (KDUMP_CMPRS_VALID()) { if (kernel_symbol_exists("crashing_cpu") && cpu_map_addr("online")) { get_symbol_data("crashing_cpu", sizeof(int), &i); if ((i >= 0) && in_cpu_map(ONLINE, i)) { if (CRASHDEBUG(1)) error(INFO, "get_diskdump_panic_task: " "active_set[%d]: %lx\n", i, tt->active_set[i]); return (tt->active_set[i]); } } } return NO_TASK; }
/* * gdb callback to access debug mode. */ int gdb_CRASHDEBUG(ulong dval) { if (CRASHDEBUG(dval)) return TRUE; return (pc->cur_req && (pc->cur_req->debug >= dval)); }
int dwarf_print_stack_entry(struct bt_info *bt, int level) { unsigned long offset; struct syment *sp; char *name; struct unwind_frame_info *frame; frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info)); UNW_SP(frame) = bt->stkptr; UNW_PC(frame) = bt->instptr; sp = value_search(UNW_PC(frame), &offset); if (!sp) { if (CRASHDEBUG(1)) fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", UNW_PC(frame)); goto bailout; } /* * If offset is zero, it means we have crossed over to the next * function. Recalculate by adjusting the text address */ if (!offset) { sp = value_search(UNW_PC(frame) - 1, &offset); if (!sp) { if (CRASHDEBUG(1)) fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", UNW_PC(frame)-1); goto bailout; } } name = sp->name; fprintf(fp, " #%d [%016lx] %s at %016lx \n", level, UNW_SP(frame), name, UNW_PC(frame)); bailout: FREEBUF(frame); return level; }
static void add_diskdump_data(char* name) { #define DDL_SIZE 16 int i; int sz = sizeof(void*); struct diskdump_data *ddp; if (dd_list == NULL) { dd_list = calloc(DDL_SIZE, sz); num_dd = DDL_SIZE; } else { for (i = 0; i < num_dumpfiles; i++) { ddp = dd_list[i]; if (same_file(ddp->filename, name)) error(FATAL, "split dumpfiles are identical:\n" " %s\n %s\n", ddp->filename, name); if (memcmp(ddp->header, dd->header, sizeof(struct disk_dump_header))) error(FATAL, "split dumpfiles derived from different vmcores:\n" " %s\n %s\n", ddp->filename, name); } } if (num_dumpfiles == num_dd) { /* expand list */ struct diskdump_data **tmp; tmp = calloc(num_dd*2, sz); memcpy(tmp, dd_list, sz*num_dd); free(dd_list); dd_list = tmp; num_dd *= 2; } dd_list[num_dumpfiles] = dd; dd->flags |= DUMPFILE_SPLIT; dd->filename = name; if (CRASHDEBUG(1)) fprintf(fp, "%s: start_pfn=%lu, end_pfn=%lu\n", name, dd->sub_header_kdump->start_pfn, dd->sub_header_kdump->end_pfn); }
/* * Try to set a crash scope block based upon the vaddr. */ int gdb_set_crash_scope(ulong vaddr, char *arg) { struct gnu_request request, *req = &request; char name[BUFSIZE]; struct load_module *lm; if (!is_kernel_text(vaddr)) { error(INFO, "invalid text address: %s\n", arg); return FALSE; } if (module_symbol(vaddr, NULL, &lm, name, 0)) { if (!(lm->mod_flags & MOD_LOAD_SYMS)) { error(INFO, "attempting to find/load \"%s\" module debuginfo\n", lm->mod_name); if (!load_module_symbols_helper(lm->mod_name)) { error(INFO, "cannot find/load \"%s\" module debuginfo\n", lm->mod_name); return FALSE; } } } req->command = GNU_SET_CRASH_BLOCK; req->addr = vaddr; req->flags = 0; req->addr2 = 0; gdb_command_funnel(req); if (CRASHDEBUG(1)) fprintf(fp, "gdb_set_crash_scope: %s addr: %lx block: %lx\n", req->flags & GNU_COMMAND_FAILED ? "FAILED" : "OK", req->addr, req->addr2); if (req->flags & GNU_COMMAND_FAILED) { error(INFO, "gdb cannot find text block for address: %s\n", arg); return FALSE; } return TRUE; }
void check_flattened_format(char *file) { int fd; struct makedumpfile_header fh; if (flattened_format) return; fd = open(file, O_RDONLY); if (fd < 0) { error(INFO, "unable to open dump file %s", file); return; } if (read(fd, &fh, sizeof(fh)) < 0) { error(INFO, "unable to read dump file %s", file); close(fd); return; } close(fd); if (!is_bigendian()){ fh.type = bswap_64(fh.type); fh.version = bswap_64(fh.version); } if ((strncmp(fh.signature, MAKEDUMPFILE_SIGNATURE, sizeof(MAKEDUMPFILE_SIGNATURE)) != 0) || (fh.type != TYPE_FLAT_HEADER)) return; if (!read_all_makedumpfile_data_header(file)) return; if (CRASHDEBUG(1)) fprintf(fp, "%s: FLAT\n\n", file); fh_save = fh; flattened_format = TRUE; }
/* * Called from main() this routine sets up the call-back hook such that * gdb's main() routine -- renamed gdb_main() -- will call back to * our main_loop() after gdb initializes. */ void gdb_main_loop(int argc, char **argv) { argc = 1; if (pc->flags & SILENT) { if (pc->flags & READNOW) argv[argc++] = "--readnow"; argv[argc++] = "--quiet"; argv[argc++] = pc->namelist_debug ? pc->namelist_debug : (pc->debuginfo_file && (st->flags & CRC_MATCHES) ? pc->debuginfo_file : pc->namelist); } else { if (pc->flags & READNOW) argv[argc++] = "--readnow"; argv[argc++] = pc->namelist_debug ? pc->namelist_debug : (pc->debuginfo_file && (st->flags & CRC_MATCHES) ? pc->debuginfo_file : pc->namelist); } if (CRASHDEBUG(1)) { int i; fprintf(fp, "gdb "); for (i = 1; i < argc; i++) fprintf(fp, "%s ", argv[i]); fprintf(fp, "\n"); } optind = 0; #if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) command_loop_hook = main_loop; #else deprecated_command_loop_hook = main_loop; #endif gdb_main_entry(argc, argv); }
static int verify_paddr(physaddr_t paddr) { int i, ok; if (!machdep->verify_paddr(paddr)) return FALSE; if (!nr_segments) return TRUE; for (i = ok = 0; i < nr_segments; i++) { if ((paddr >= ram_segments[i].start) && (paddr < ram_segments[i].end)) { ok++; break; } } /* * Pre-2.6.13 x86_64 /proc/iomem was restricted to 4GB, * so just accept it. */ if ((paddr >= 0x100000000ULL) && machine_type("X86_64") && (THIS_KERNEL_VERSION < LINUX(2,6,13))) ok++; if (!ok) { if (CRASHDEBUG(1)) console("reject: %llx\n", (ulonglong)paddr); return FALSE; } return TRUE; }
/* * General purpose routine for passing commands to gdb. All gdb commands * come through here, where they are passed to gdb_command_funnel(). */ void gdb_interface(struct gnu_request *req) { if (!(pc->flags & GDB_INIT)) error(FATAL, "gdb_interface: gdb not initialized?\n"); if (output_closed()) restart(0); if (!req->fp) { req->fp = ((pc->flags & RUNTIME) || (pc->flags2 & ALLOW_FP)) ? fp : CRASHDEBUG(1) ? fp : pc->nullfp; } pc->cur_req = req; pc->cur_gdb_cmd = req->command; if (req->flags & GNU_RETURN_ON_ERROR) { error_hook = gdb_error_hook; if (setjmp(pc->gdb_interface_env)) { pc->last_gdb_cmd = pc->cur_gdb_cmd; pc->cur_gdb_cmd = 0; pc->cur_req = NULL; req->flags |= GNU_COMMAND_FAILED; pc->flags &= ~IN_GDB; return; } } else error_hook = NULL; if (CRASHDEBUG(2)) dump_gnu_request(req, IN_GDB); if (!(pc->flags & DROP_CORE)) SIGACTION(SIGSEGV, restart, &pc->sigaction, NULL); else SIGACTION(SIGSEGV, SIG_DFL, &pc->sigaction, NULL); if (interruptible()) { SIGACTION(SIGINT, pc->gdb_sigaction.sa_handler, &pc->gdb_sigaction, NULL); } else { SIGACTION(SIGINT, SIG_IGN, &pc->sigaction, NULL); SIGACTION(SIGPIPE, SIG_IGN, &pc->sigaction, NULL); } pc->flags |= IN_GDB; gdb_command_funnel(req); pc->flags &= ~IN_GDB; SIGACTION(SIGINT, restart, &pc->sigaction, NULL); SIGACTION(SIGSEGV, SIG_DFL, &pc->sigaction, NULL); if (CRASHDEBUG(2)) dump_gnu_request(req, !IN_GDB); error_hook = NULL; pc->last_gdb_cmd = pc->cur_gdb_cmd; pc->cur_gdb_cmd = 0; pc->cur_req = NULL; }
/* * Read from a diskdump-created dumpfile. */ int read_diskdump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr) { int ret; physaddr_t curpaddr; ulong pfn, page_offset; pfn = paddr_to_pfn(paddr); if (KDUMP_SPLIT()) { /* Find proper dd */ int i; unsigned long start_pfn; unsigned long end_pfn; for (i=0; i<num_dumpfiles; i++) { start_pfn = dd_list[i]->sub_header_kdump->start_pfn; end_pfn = dd_list[i]->sub_header_kdump->end_pfn; if ((pfn >= start_pfn) && (pfn <= end_pfn)) { dd = dd_list[i]; break; } } if (i == num_dumpfiles) { if (CRASHDEBUG(8)) fprintf(fp, "read_diskdump: SEEK_ERROR: " "paddr/pfn %llx/%lx beyond last dumpfile\n", (ulonglong)paddr, pfn); return SEEK_ERROR; } } curpaddr = paddr & ~((physaddr_t)(dd->block_size-1)); page_offset = paddr & ((physaddr_t)(dd->block_size-1)); if ((pfn >= dd->header->max_mapnr) || !page_is_ram(pfn)) { if (CRASHDEBUG(8)) { fprintf(fp, "read_diskdump: SEEK_ERROR: " "paddr/pfn: %llx/%lx ", (ulonglong)paddr, pfn); if (pfn >= dd->header->max_mapnr) fprintf(fp, "max_mapnr: %x\n", dd->header->max_mapnr); else fprintf(fp, "!page_is_ram\n"); } return SEEK_ERROR; } if (!page_is_dumpable(pfn)) { if ((dd->flags & (ZERO_EXCLUDED|ERROR_EXCLUDED)) == ERROR_EXCLUDED) { if (CRASHDEBUG(8)) fprintf(fp, "read_diskdump: PAGE_EXCLUDED: " "paddr/pfn: %llx/%lx\n", (ulonglong)paddr, pfn); return PAGE_EXCLUDED; } if (CRASHDEBUG(8)) fprintf(fp, "read_diskdump: zero-fill: " "paddr/pfn: %llx/%lx\n", (ulonglong)paddr, pfn); memset(bufptr, 0, cnt); return cnt; } if (!page_is_cached(curpaddr)) { if (CRASHDEBUG(8)) fprintf(fp, "read_diskdump: paddr/pfn: %llx/%lx" " -> cache physical page: %llx\n", (ulonglong)paddr, pfn, (ulonglong)curpaddr); if ((ret = cache_page(curpaddr)) < 0) { if (CRASHDEBUG(8)) fprintf(fp, "read_diskdump: " "%s: cannot cache page: %llx\n", ret == SEEK_ERROR ? "SEEK_ERROR" : "READ_ERROR", (ulonglong)curpaddr); return ret; } } else if (CRASHDEBUG(8)) fprintf(fp, "read_diskdump: paddr/pfn: %llx/%lx" " -> physical page is cached: %llx\n", (ulonglong)paddr, pfn, (ulonglong)curpaddr); memcpy(bufptr, dd->curbufptr + page_offset, cnt); return cnt; }
static char *write_elf(Elf64_Phdr *load, Elf64_Ehdr *e_head, size_t data_offset) { #define CPY_BUF_SZ 4096 int fd1, fd2, i, err = 1; char *buf; char *out_elf; size_t offset; ssize_t rd, len; buf = (char *)malloc(CPY_BUF_SZ); offset = data_offset; if (user_elf) { fd2 = open(user_elf, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); if (fd2 < 0) { error(INFO, "%s open error, %s\n", user_elf, strerror(errno)); goto end1; } out_elf = user_elf; } else { fd2 = mkstemp(elf_default); if (fd2 < 0) { error(INFO, "%s open error, %s\n", elf_default, strerror(errno)); goto end1; } out_elf = elf_default; pc->flags2 |= RAMDUMP; } if (user_elf) { sprintf(buf, "creating ELF dumpfile: %s", out_elf); please_wait(buf); } else if (CRASHDEBUG(1)) fprintf(fp, "creating temporary ELF header: %s\n\n", elf_default); while (offset > 0) { len = write(fd2, e_head + (data_offset - offset), offset); if (len < 0) { error(INFO, "ramdump write error, %s\n", strerror(errno)); goto end; } offset -= len; } if (user_elf) { for (i = 0; i < nodes; i++) { offset = load[i].p_offset; fd1 = open(ramdump[i].path, O_RDONLY, S_IRUSR); if (fd1 < 0) { error(INFO, "%s open error, %s\n", ramdump[i].path, strerror(errno)); goto end; } lseek(fd2, (off_t)offset, SEEK_SET); while ((rd = read(fd1, buf, CPY_BUF_SZ)) > 0) { if (write(fd2, buf, rd) != rd) { error(INFO, "%s write error, %s\n", ramdump[i].path, strerror(errno)); close(fd1); goto end; } } close(fd1); } please_wait_done(); } err = 0; end: close(fd2); end1: free(buf); return err ? NULL : out_elf; }
static int read_dump_header(char *file) { struct disk_dump_header *header = NULL; struct disk_dump_sub_header *sub_header = NULL; struct kdump_sub_header *sub_header_kdump = NULL; size_t size; int bitmap_len; int block_size = (int)sysconf(_SC_PAGESIZE); off_t offset; const off_t failed = (off_t)-1; ulong pfn; int i, j, max_sect_len; int is_split = 0; if (block_size < 0) return FALSE; restart: if ((header = realloc(header, block_size)) == NULL) error(FATAL, "diskdump / compressed kdump: cannot malloc block_size buffer\n"); if (FLAT_FORMAT()) { if (!read_flattened_format(dd->dfd, 0, header, block_size)) { error(FATAL, "diskdump / compressed kdump: cannot read header\n"); goto err; } } else { if (lseek(dd->dfd, 0, SEEK_SET) == failed) { if (CRASHDEBUG(1)) error(INFO, "diskdump / compressed kdump: cannot lseek dump header\n"); goto err; } if (read(dd->dfd, header, block_size) < block_size) { if (CRASHDEBUG(1)) error(INFO, "diskdump / compressed kdump: cannot read dump header\n"); goto err; } } /* validate dump header */ if (!memcmp(header->signature, DISK_DUMP_SIGNATURE, sizeof(header->signature))) { dd->flags |= DISKDUMP_LOCAL; } else if (!memcmp(header->signature, KDUMP_SIGNATURE, sizeof(header->signature))) { dd->flags |= KDUMP_CMPRS_LOCAL; if (header->header_version >= 1) dd->flags |= ERROR_EXCLUDED; } else { if (CRASHDEBUG(1)) error(INFO, "diskdump / compressed kdump: dump does not have panic dump header\n"); goto err; } if (CRASHDEBUG(1)) fprintf(fp, "%s: header->utsname.machine: %s\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump", header->utsname.machine); if (STRNEQ(header->utsname.machine, "i686") && machine_type_mismatch(file, "X86", NULL, 0)) goto err; else if (STRNEQ(header->utsname.machine, "x86_64") && machine_type_mismatch(file, "X86_64", NULL, 0)) goto err; else if (STRNEQ(header->utsname.machine, "ia64") && machine_type_mismatch(file, "IA64", NULL, 0)) goto err; else if (STREQ(header->utsname.machine, "ppc") && machine_type_mismatch(file, "PPC", NULL, 0)) goto err; else if (STREQ(header->utsname.machine, "ppc64") && machine_type_mismatch(file, "PPC64", NULL, 0)) goto err; else if (STRNEQ(header->utsname.machine, "arm") && machine_type_mismatch(file, "ARM", NULL, 0)) goto err; else if (STRNEQ(header->utsname.machine, "s390x") && machine_type_mismatch(file, "S390X", NULL, 0)) goto err; if (header->block_size != block_size) { block_size = header->block_size; if (CRASHDEBUG(1)) fprintf(fp, "retrying with different block/page size: %d\n", header->block_size); goto restart; } dd->block_size = header->block_size; dd->block_shift = ffs(header->block_size) - 1; if ((DISKDUMP_VALID() && (sizeof(*header) + sizeof(void *) * header->nr_cpus > block_size)) || header->nr_cpus <= 0) { error(INFO, "%s: invalid nr_cpus value: %d\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump", header->nr_cpus); if (!machine_type("S390") && !machine_type("S390X")) { /* s390 can get register information also from memory */ goto err; } } /* read sub header */ offset = (off_t)block_size; if (DISKDUMP_VALID()) { if ((sub_header = malloc(block_size)) == NULL) error(FATAL, "diskdump: cannot malloc sub_header buffer\n"); if (FLAT_FORMAT()) { if (!read_flattened_format(dd->dfd, offset, sub_header, block_size)) { error(INFO, "diskdump: cannot read dump sub header\n"); goto err; } } else { if (lseek(dd->dfd, offset, SEEK_SET) == failed) { error(INFO, "diskdump: cannot lseek dump sub header\n"); goto err; } if (read(dd->dfd, sub_header, block_size) < block_size) { error(INFO, "diskdump: cannot read dump sub header\n"); goto err; } } dd->sub_header = sub_header; } else if (KDUMP_CMPRS_VALID()) { if ((sub_header_kdump = malloc(block_size)) == NULL) error(FATAL, "compressed kdump: cannot malloc sub_header_kdump buffer\n"); if (FLAT_FORMAT()) { if (!read_flattened_format(dd->dfd, offset, sub_header_kdump, block_size)) { error(INFO, "compressed kdump: cannot read dump sub header\n"); goto err; } } else { if (lseek(dd->dfd, offset, SEEK_SET) == failed) { error(INFO, "compressed kdump: cannot lseek dump sub header\n"); goto err; } if (read(dd->dfd, sub_header_kdump, block_size) < block_size) { error(INFO, "compressed kdump: cannot read dump sub header\n"); goto err; } } dd->sub_header_kdump = sub_header_kdump; } /* read memory bitmap */ bitmap_len = block_size * header->bitmap_blocks; dd->bitmap_len = bitmap_len; offset = (off_t)block_size * (1 + header->sub_hdr_size); if ((dd->bitmap = malloc(bitmap_len)) == NULL) error(FATAL, "%s: cannot malloc bitmap buffer\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); dd->dumpable_bitmap = calloc(bitmap_len, 1); if (CRASHDEBUG(8)) fprintf(fp, "%s: memory bitmap offset: %llx\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump", (ulonglong)offset); if (FLAT_FORMAT()) { if (!read_flattened_format(dd->dfd, offset, dd->bitmap, bitmap_len)) { error(INFO, "%s: cannot read memory bitmap\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); goto err; } } else { if (lseek(dd->dfd, offset, SEEK_SET) == failed) { error(INFO, "%s: cannot lseek memory bitmap\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); goto err; } if (read(dd->dfd, dd->bitmap, bitmap_len) < bitmap_len) { error(INFO, "%s: cannot read memory bitmap\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump"); goto err; } } if (dump_is_partial(header)) memcpy(dd->dumpable_bitmap, dd->bitmap + bitmap_len/2, bitmap_len/2); else memcpy(dd->dumpable_bitmap, dd->bitmap, bitmap_len); dd->data_offset = (1 + header->sub_hdr_size + header->bitmap_blocks) * header->block_size; dd->header = header; if (machine_type("ARM")) dd->machine_type = EM_ARM; else if (machine_type("X86")) dd->machine_type = EM_386; else if (machine_type("X86_64")) dd->machine_type = EM_X86_64; else if (machine_type("IA64")) dd->machine_type = EM_IA_64; else if (machine_type("PPC")) dd->machine_type = EM_PPC; else if (machine_type("PPC64")) dd->machine_type = EM_PPC64; else if (machine_type("S390X")) dd->machine_type = EM_S390; else { error(INFO, "%s: unsupported machine type: %s\n", DISKDUMP_VALID() ? "diskdump" : "compressed kdump", MACHINE_TYPE); goto err; } /* process elf notes data */ if (KDUMP_CMPRS_VALID() && !(dd->flags & NO_ELF_NOTES) && (dd->header->header_version >= 4) && (sub_header_kdump->offset_note) && (sub_header_kdump->size_note) && (machdep->process_elf_notes)) { size = sub_header_kdump->size_note; offset = sub_header_kdump->offset_note; if ((dd->notes_buf = malloc(size)) == NULL) error(FATAL, "compressed kdump: cannot malloc notes" " buffer\n"); if ((dd->nt_prstatus_percpu = malloc(NR_CPUS * sizeof(void*))) == NULL) error(FATAL, "compressed kdump: cannot malloc pointer" " to NT_PRSTATUS notes\n"); if (FLAT_FORMAT()) { if (!read_flattened_format(dd->dfd, offset, dd->notes_buf, size)) { error(INFO, "compressed kdump: cannot read notes data" "\n"); goto err; } } else { if (lseek(dd->dfd, offset, SEEK_SET) == failed) { error(INFO, "compressed kdump: cannot lseek notes data\n"); goto err; } if (read(dd->dfd, dd->notes_buf, size) < size) { error(INFO, "compressed kdump: cannot read notes data" "\n"); goto err; } } machdep->process_elf_notes(dd->notes_buf, size); } /* Check if dump file contains erasesinfo data */ if (KDUMP_CMPRS_VALID() && (dd->header->header_version >= 5) && (sub_header_kdump->offset_eraseinfo) && (sub_header_kdump->size_eraseinfo)) pc->flags2 |= ERASEINFO_DATA; /* For split dumpfile */ if (KDUMP_CMPRS_VALID()) { is_split = ((dd->header->header_version >= 2) && (sub_header_kdump->split)); if ((is_split && (num_dumpfiles != 0) && (dd_list == NULL))|| (!is_split && (num_dumpfiles != 0))) { clean_diskdump_data(); goto err; } if (is_split) add_diskdump_data(file); num_dumpfiles++; } if (!is_split) { max_sect_len = divideup(header->max_mapnr, BITMAP_SECT_LEN); pfn = 0; dd->filename = file; } else { ulong start = sub_header_kdump->start_pfn; ulong end = sub_header_kdump->end_pfn; max_sect_len = divideup(end - start + 1, BITMAP_SECT_LEN); pfn = start; } dd->valid_pages = calloc(sizeof(ulong), max_sect_len + 1); for (i = 1; i < max_sect_len + 1; i++) { dd->valid_pages[i] = dd->valid_pages[i - 1]; for (j = 0; j < BITMAP_SECT_LEN; j++, pfn++) if (page_is_dumpable(pfn)) dd->valid_pages[i]++; } return TRUE; err: free(header); if (sub_header) free(sub_header); if (sub_header_kdump) free(sub_header_kdump); if (dd->bitmap) free(dd->bitmap); if (dd->dumpable_bitmap) free(dd->dumpable_bitmap); if (dd->notes_buf) free(dd->notes_buf); if (dd->nt_prstatus_percpu) free(dd->nt_prstatus_percpu); dd->flags &= ~(DISKDUMP_LOCAL|KDUMP_CMPRS_LOCAL); pc->flags2 &= ~ELF_NOTES; return FALSE; }
int dwarf_backtrace(struct bt_info *bt, int level, ulong stacktop) { unsigned long bp, offset; struct syment *sp; char *name; struct unwind_frame_info *frame; int is_ehframe = (!st->dwarf_debug_frame_size && st->dwarf_eh_frame_size); frame = (struct unwind_frame_info *)GETBUF(sizeof(struct unwind_frame_info)); // frame->regs.rsp = bt->stkptr; // frame->regs.rip = bt->instptr; UNW_SP(frame) = bt->stkptr; UNW_PC(frame) = bt->instptr; /* read rbp from stack for non active tasks */ if (!(bt->flags & BT_DUMPFILE_SEARCH) && !bt->bptr) { // readmem(frame->regs.rsp, KVADDR, &bp, readmem(UNW_SP(frame), KVADDR, &bp, sizeof(unsigned long), "reading bp", FAULT_ON_ERROR); frame->regs.rbp = bp; /* fixme for x86 */ } sp = value_search(UNW_PC(frame), &offset); if (!sp) { if (CRASHDEBUG(1)) fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", UNW_PC(frame)); goto bailout; } /* * If offset is zero, it means we have crossed over to the next * function. Recalculate by adjusting the text address */ if (!offset) { sp = value_search(UNW_PC(frame) - 1, &offset); if (!sp) { if (CRASHDEBUG(1)) fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", UNW_PC(frame)-1); goto bailout; } } name = sp->name; fprintf(fp, " #%d [%016lx] %s at %016lx \n", level, UNW_SP(frame), name, UNW_PC(frame)); if (CRASHDEBUG(2)) fprintf(fp, " < SP: %lx PC: %lx FP: %lx >\n", UNW_SP(frame), UNW_PC(frame), frame->regs.rbp); while ((UNW_SP(frame) < stacktop) && !unwind(frame, is_ehframe) && UNW_PC(frame)) { /* To prevent rip pushed on IRQ stack being reported both * both on the IRQ and process stacks */ if ((bt->flags & BT_IRQSTACK) && (UNW_SP(frame) >= stacktop - 16)) break; level++; sp = value_search(UNW_PC(frame), &offset); if (!sp) { if (CRASHDEBUG(1)) fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", UNW_PC(frame)); break; } /* * If offset is zero, it means we have crossed over to the next * function. Recalculate by adjusting the text address */ if (!offset) { sp = value_search(UNW_PC(frame) - 1, &offset); if (!sp) { if (CRASHDEBUG(1)) fprintf(fp, "unwind: cannot find symbol for PC: %lx\n", UNW_PC(frame)-1); goto bailout; } } name = sp->name; fprintf(fp, "%s#%d [%016lx] %s at %016lx \n", level < 10 ? " " : "", level, UNW_SP(frame), name, UNW_PC(frame)); if (CRASHDEBUG(2)) fprintf(fp, " < SP: %lx PC: %lx FP: %lx >\n", UNW_SP(frame), UNW_PC(frame), frame->regs.rbp); } bailout: FREEBUF(frame); return ++level; }
/* * Initialize the unwind table(s) in the best-case order: * * 1. Use the in-memory kernel and module unwind tables. * 2. Use the in-memory kernel-only .eh_frame data. (possible?) * 3. Use the kernel-only .eh_frame data from the vmlinux file. */ void init_unwind_table(void) { ulong unwind_table_size; void *unwind_table; kt->flags &= ~DWARF_UNWIND; if (gather_in_memory_unwind_tables()) { if (CRASHDEBUG(1)) fprintf(fp, "init_unwind_table: DWARF_UNWIND_MEMORY (%d tables)\n", unwind_tables_cnt); kt->flags |= DWARF_UNWIND_MEMORY; if (unwind_tables_cnt > 1) kt->flags |= DWARF_UNWIND_MODULES; if (!(kt->flags & NO_DWARF_UNWIND)) kt->flags |= DWARF_UNWIND; return; } if (symbol_exists("__start_unwind") && symbol_exists("__end_unwind")) { unwind_table_size = symbol_value("__end_unwind") - symbol_value("__start_unwind"); if (!(unwind_table = malloc(unwind_table_size))) { error(WARNING, "cannot malloc unwind table space\n"); goto try_eh_frame; } if (!readmem(symbol_value("__start_unwind"), KVADDR, unwind_table, unwind_table_size, "unwind table", RETURN_ON_ERROR)) { error(WARNING, "cannot read unwind table data\n"); free(unwind_table); goto try_eh_frame; } kt->flags |= DWARF_UNWIND_MEMORY; if (!(kt->flags & NO_DWARF_UNWIND)) kt->flags |= DWARF_UNWIND; default_unwind_table.size = unwind_table_size; default_unwind_table.address = unwind_table; if (CRASHDEBUG(1)) fprintf(fp, "init_unwind_table: DWARF_UNWIND_MEMORY\n"); return; } try_eh_frame: if (st->dwarf_eh_frame_size || st->dwarf_debug_frame_size) { int fd; int is_ehframe = (!st->dwarf_debug_frame_size && st->dwarf_eh_frame_size); unwind_table_size = is_ehframe ? st->dwarf_eh_frame_size : st->dwarf_debug_frame_size; if (!(unwind_table = malloc(unwind_table_size))) { error(WARNING, "cannot malloc unwind table space\n"); return; } if ((fd = open(pc->namelist, O_RDONLY)) < 0) { error(WARNING, "cannot open %s for %s data\n", pc->namelist, is_ehframe ? ".eh_frame" : ".debug_frame"); free(unwind_table); return; } if (is_ehframe) lseek(fd, st->dwarf_eh_frame_file_offset, SEEK_SET); else lseek(fd, st->dwarf_debug_frame_file_offset, SEEK_SET); if (read(fd, unwind_table, unwind_table_size) != unwind_table_size) { if (CRASHDEBUG(1)) error(WARNING, "cannot read %s data from %s\n", is_ehframe ? ".eh_frame" : ".debug_frame", pc->namelist); free(unwind_table); return; } close(fd); default_unwind_table.size = unwind_table_size; default_unwind_table.address = unwind_table; kt->flags |= DWARF_UNWIND_EH_FRAME; if (!(kt->flags & NO_DWARF_UNWIND)) kt->flags |= DWARF_UNWIND; if (CRASHDEBUG(1)) fprintf(fp, "init_unwind_table: DWARF_UNWIND_EH_FRAME\n"); return; } }
/* * Find the appropriate kernel-only "root_table" unwind_table, * and pass it to populate_local_tables() to do the heavy lifting. */ static int gather_in_memory_unwind_tables(void) { int i, cnt, found; struct syment *sp, *root_tables[10]; char *root_table_buf; char buf[BUFSIZE]; ulong name; STRUCT_SIZE_INIT(unwind_table, "unwind_table"); MEMBER_OFFSET_INIT(unwind_table_core, "unwind_table", "core"); MEMBER_OFFSET_INIT(unwind_table_init, "unwind_table", "init"); MEMBER_OFFSET_INIT(unwind_table_address, "unwind_table", "address"); MEMBER_OFFSET_INIT(unwind_table_size, "unwind_table", "size"); MEMBER_OFFSET_INIT(unwind_table_link, "unwind_table", "link"); MEMBER_OFFSET_INIT(unwind_table_name, "unwind_table", "name"); if (INVALID_SIZE(unwind_table) || INVALID_MEMBER(unwind_table_core) || INVALID_MEMBER(unwind_table_init) || INVALID_MEMBER(unwind_table_address) || INVALID_MEMBER(unwind_table_size) || INVALID_MEMBER(unwind_table_link) || INVALID_MEMBER(unwind_table_name)) { if (CRASHDEBUG(1)) error(NOTE, "unwind_table structure has changed, or does not exist in this kernel\n"); return 0; } /* * Unfortunately there are two kernel root_table symbols. */ if (!(cnt = get_syment_array("root_table", root_tables, 10))) return 0; root_table_buf = GETBUF(SIZE(unwind_table)); for (i = found = 0; i < cnt; i++) { sp = root_tables[i]; if (!readmem(sp->value, KVADDR, root_table_buf, SIZE(unwind_table), "root unwind_table", RETURN_ON_ERROR|QUIET)) goto gather_failed; name = ULONG(root_table_buf + OFFSET(unwind_table_name)); if (read_string(name, buf, strlen("kernel")+1) && STREQ("kernel", buf)) { found++; if (CRASHDEBUG(1)) fprintf(fp, "root_table name: %lx [%s]\n", name, buf); break; } } if (!found) goto gather_failed; cnt = populate_local_tables(sp->value, root_table_buf); FREEBUF(root_table_buf); return cnt; gather_failed: FREEBUF(root_table_buf); return 0; }
/* * Transfer the relevant data from the kernel and module unwind_table * structures to the local_unwind_table structures. */ static int populate_local_tables(ulong root, char *buf) { struct list_data list_data, *ld; int i, cnt; ulong *table_list; ulong vaddr; struct local_unwind_table *tp; ld = &list_data; BZERO(ld, sizeof(struct list_data)); ld->start = root; ld->member_offset = OFFSET(unwind_table_link); ld->flags = RETURN_ON_LIST_ERROR; if (CRASHDEBUG(1)) ld->flags |= VERBOSE; hq_open(); cnt = do_list(ld); if (cnt == -1) { error(WARNING, "UNWIND: failed to gather unwind_table list"); return 0; } table_list = (ulong *)GETBUF(cnt * sizeof(ulong)); cnt = retrieve_list(table_list, cnt); hq_close(); if (!(local_unwind_tables = malloc(sizeof(struct local_unwind_table) * cnt))) { error(WARNING, "cannot malloc unwind_table space (%d tables)\n", cnt); FREEBUF(table_list); return 0; } for (i = 0; i < cnt; i++, tp++) { if (!readmem(table_list[i], KVADDR, buf, SIZE(unwind_table), "unwind_table", RETURN_ON_ERROR|QUIET)) { error(WARNING, "cannot read unwind_table\n"); goto failed; } tp = &local_unwind_tables[i]; /* * Copy the required table info for find_table(). */ BCOPY(buf + OFFSET(unwind_table_core), (char *)&tp->core.pc, sizeof(ulong)*2); BCOPY(buf + OFFSET(unwind_table_init), (char *)&tp->init.pc, sizeof(ulong)*2); BCOPY(buf + OFFSET(unwind_table_size), (char *)&tp->size, sizeof(ulong)); /* * Then read the DWARF CFI data. */ vaddr = ULONG(buf + OFFSET(unwind_table_address)); if (!(tp->address = malloc(tp->size))) { error(WARNING, "cannot malloc unwind_table space\n"); goto failed; break; } if (!readmem(vaddr, KVADDR, tp->address, tp->size, "DWARF CFI data", RETURN_ON_ERROR|QUIET)) { error(WARNING, "cannot read unwind_table data\n"); goto failed; } } unwind_tables_cnt = cnt; if (CRASHDEBUG(7)) dump_local_unwind_tables(); failed: FREEBUF(table_list); return unwind_tables_cnt; }
/* * The gdb target_xfer_memory() has a hook installed to re-route * all memory accesses back here; reads of 1 or 4 bytes come primarily * from text disassembly requests, and are diverted to the text cache. */ int gdb_readmem_callback(ulong addr, void *buf, int len, int write) { char locbuf[SIZEOF_32BIT], *p1; uint32_t *p2; int memtype; if (write) return FALSE; if (pc->cur_req->flags & GNU_NO_READMEM) return TRUE; if (pc->curcmd_flags & MEMTYPE_UVADDR) memtype = UVADDR; else if (pc->curcmd_flags & MEMTYPE_FILEADDR) memtype = FILEADDR; else if (!IS_KVADDR(addr)) { if (STREQ(pc->curcmd, "gdb") && STRNEQ(pc->cur_req->buf, "x/")) { memtype = UVADDR; } else { if (CRASHDEBUG(1)) console("gdb_readmem_callback: %lx %d FAILED\n", addr, len); return FALSE; } } else memtype = KVADDR; if (CRASHDEBUG(1)) console("gdb_readmem_callback[%d]: %lx %d\n", memtype, addr, len); if (memtype == FILEADDR) return(readmem(pc->curcmd_private, memtype, buf, len, "gdb_readmem_callback", RETURN_ON_ERROR)); switch (len) { case SIZEOF_8BIT: p1 = (char *)buf; if ((memtype == KVADDR) && text_value_cache_byte(addr, (unsigned char *)p1)) return TRUE; if (!readmem(addr, memtype, locbuf, SIZEOF_32BIT, "gdb_readmem_callback", RETURN_ON_ERROR)) return FALSE; *p1 = locbuf[0]; if (memtype == KVADDR) { p2 = (uint32_t *)locbuf; text_value_cache(addr, *p2, 0); } return TRUE; case SIZEOF_32BIT: if ((memtype == KVADDR) && text_value_cache(addr, 0, buf)) return TRUE; if (!readmem(addr, memtype, buf, SIZEOF_32BIT, "gdb_readmem callback", RETURN_ON_ERROR)) return FALSE; if (memtype == KVADDR) text_value_cache(addr, (uint32_t)*((uint32_t *)buf), NULL); return TRUE; } return(readmem(addr, memtype, buf, len, "gdb_readmem_callback", RETURN_ON_ERROR)); }
void gdb_session_init(void) { struct gnu_request *req; int debug_data_pulled_in; if (!have_partial_symbols() && !have_full_symbols()) no_debugging_data(FATAL); /* * Restore the SIGINT and SIGPIPE handlers, which got temporarily * re-assigned by gdb. The SIGINT call also initializes GDB's * SIGINT sigaction. */ SIGACTION(SIGINT, restart, &pc->sigaction, &pc->gdb_sigaction); SIGACTION(SIGPIPE, SIG_IGN, &pc->sigaction, NULL); if (!(pc->flags & DROP_CORE)) SIGACTION(SIGSEGV, restart, &pc->sigaction, NULL); /* * Set up pointers to gdb variables. */ #if defined(GDB_5_3) || defined(GDB_6_0) || defined(GDB_6_1) gdb_output_format = &output_format; gdb_print_max = &print_max; gdb_prettyprint_structs = &prettyprint_structs; gdb_prettyprint_arrays = &prettyprint_arrays; gdb_repeat_count_threshold = &repeat_count_threshold; gdb_stop_print_at_null = &stop_print_at_null; gdb_output_radix = &output_radix; #else gdb_output_format = (int *) gdb_user_print_option_address("output_format"); gdb_print_max = (unsigned int *) gdb_user_print_option_address("print_max"); gdb_prettyprint_structs = (int *) gdb_user_print_option_address("prettyprint_structs"); gdb_prettyprint_arrays = (int *) gdb_user_print_option_address("prettyprint_arrays"); gdb_repeat_count_threshold = (int *) gdb_user_print_option_address("repeat_count_threshold"); gdb_stop_print_at_null = (int *) gdb_user_print_option_address("stop_print_at_null"); gdb_output_radix = (unsigned int *) gdb_user_print_option_address("output_radix"); #endif /* * If the output radix is set via the --hex or --dec command line * option, then pc->output_radix will be non-zero; otherwise use * the gdb default. */ if (pc->output_radix) { *gdb_output_radix = pc->output_radix; *gdb_output_format = (*gdb_output_radix == 10) ? 0 : 'x'; } switch (*gdb_output_radix) { case 10: case 16: pc->output_radix = *gdb_output_radix; break; default: pc->output_radix = *gdb_output_radix = 10; *gdb_output_format = 0; } *gdb_prettyprint_structs = 1; *gdb_repeat_count_threshold = 0x7fffffff; *gdb_print_max = 256; #ifdef GDB_5_3 gdb_disassemble_from_exec = 0; #endif pc->flags |= GDB_INIT; /* set here so gdb_interface will work */ req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request)); req->buf = GETBUF(BUFSIZE); /* * Make sure the namelist has symbolic data. Later versions of * gcc may require that debug data be pulled in by printing a * static kernel data structure. */ debug_data_pulled_in = FALSE; retry: BZERO(req->buf, BUFSIZE); req->command = GNU_GET_DATATYPE; req->name = XEN_HYPER_MODE() ? "page_info" : "task_struct"; req->flags = GNU_RETURN_ON_ERROR; gdb_interface(req); if (req->flags & GNU_COMMAND_FAILED) { if (XEN_HYPER_MODE()) no_debugging_data(WARNING); /* just bail out */ if (!debug_data_pulled_in) { if (CRASHDEBUG(1)) error(INFO, "gdb_session_init: pulling in debug data by accessing init_mm.mmap %s\n", symbol_exists("sysfs_mount") ? "and syfs_mount" : ""); debug_data_pulled_in = TRUE; req->command = GNU_PASS_THROUGH; req->flags = GNU_RETURN_ON_ERROR|GNU_NO_READMEM; req->name = NULL; if (symbol_exists("sysfs_mount")) sprintf(req->buf, "print sysfs_mount, init_mm.mmap"); else sprintf(req->buf, "print init_mm.mmap"); gdb_interface(req); if (!(req->flags & GNU_COMMAND_FAILED)) goto retry; } no_debugging_data(WARNING); } if (pc->flags & KERNEL_DEBUG_QUERY) { fprintf(fp, "\n%s: %s: contains debugging data\n\n", pc->program_name, pc->namelist); if (REMOTE()) remote_exit(); clean_exit(0); } /* * Set up any pre-ordained gdb settings here that can't be * accessed directly. */ req->command = GNU_PASS_THROUGH; req->name = NULL, req->flags = 0; sprintf(req->buf, "set height 0"); gdb_interface(req); req->command = GNU_PASS_THROUGH; req->name = NULL, req->flags = 0; sprintf(req->buf, "set width 0"); gdb_interface(req); /* * 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) || (kt->flags & (RELOC_SET|RELOC_FORCE)) || (pc->namelist_debug && !pc->debuginfo_file)) { req->command = GNU_PATCH_SYMBOL_VALUES; req->flags = GNU_RETURN_ON_ERROR; gdb_interface(req); if (req->flags & GNU_COMMAND_FAILED) error(FATAL, "patching of gdb symbol values failed\n"); } else if (!(pc->flags & SILENT)) fprintf(fp, "\n"); FREEBUF(req->buf); FREEBUF(req); }