struct thread* unwind_stacks(Dwfl *dwfl, const char *core_file, struct exec_map *em, struct expr_context *ctx) { unw_addr_space_t as; struct UCD_info *ui; struct thread *head = NULL, *tail = NULL; as = unw_create_addr_space(&_UCD_accessors, 0); fail_if(!as, "unw_create_addr_space"); ui = _UCD_create(core_file); fail_if(!ui, "_UCD_create"); for (; em != NULL; em = em->next) { if (_UCD_add_backing_file_at_vaddr(ui, em->vaddr, em->file) < 0) { fail("_UCD_add_backing_file_at_vaddr"); } } int tnum; int nthreads = _UCD_get_num_threads(ui); for (tnum = 0; tnum < nthreads; tnum++) { struct thread *thread = xalloc(sizeof(struct thread)); thread->frames = unwind_thread(dwfl, as, ui, tnum, ctx); list_append(head, tail, thread); } return head; }
int main(int argc UNUSED, char **argv) { unw_addr_space_t as; struct UCD_info *ui; unw_cursor_t c; int ret; #define TEST_FRAMES 4 #define TEST_NAME_LEN 32 int testcase = 0; int test_cur = 0; long test_start_ips[TEST_FRAMES]; char test_names[TEST_FRAMES][TEST_NAME_LEN]; install_sigsegv_handler(); const char *progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; if (!argv[1]) error_msg_and_die("Usage: %s COREDUMP [VADDR:BINARY_FILE]...", progname); msg_prefix = progname; as = unw_create_addr_space(&_UCD_accessors, 0); if (!as) error_msg_and_die("unw_create_addr_space() failed"); ui = _UCD_create(argv[1]); if (!ui) error_msg_and_die("_UCD_create('%s') failed", argv[1]); ret = unw_init_remote(&c, as, ui); if (ret < 0) error_msg_and_die("unw_init_remote() failed: ret=%d\n", ret); argv += 2; /* Enable checks for the crasher test program? */ if (*argv && !strcmp(*argv, "-testcase")) { testcase = 1; logmode = LOGMODE_NONE; argv++; } while (*argv) { char *colon; unsigned long vaddr = strtoul(*argv, &colon, 16); if (*colon != ':') error_msg_and_die("Bad format: '%s'", *argv); if (_UCD_add_backing_file_at_vaddr(ui, vaddr, colon + 1) < 0) error_msg_and_die("Can't add backing file '%s'", colon + 1); argv++; } for (;;) { unw_word_t ip; ret = unw_get_reg(&c, UNW_REG_IP, &ip); if (ret < 0) error_msg_and_die("unw_get_reg(UNW_REG_IP) failed: ret=%d\n", ret); unw_proc_info_t pi; ret = unw_get_proc_info(&c, &pi); if (ret < 0) error_msg_and_die("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret); if (!testcase) printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx\n", (long) ip, (long) pi.start_ip, (long) pi.end_ip, (long) pi.handler, (long) pi.lsda); if (testcase && test_cur < TEST_FRAMES) { unw_word_t off; test_start_ips[test_cur] = (long) pi.start_ip; if (unw_get_proc_name(&c, test_names[test_cur], sizeof(test_names[0]), &off) != 0) { test_names[test_cur][0] = '\0'; } test_cur++; } log("step"); ret = unw_step(&c); log("step done:%d", ret); if (ret < 0) error_msg_and_die("FAILURE: unw_step() returned %d", ret); if (ret == 0) break; } log("stepping ended"); /* Check that the second and third frames are equal, but distinct of the * others */ if (testcase && (test_cur != 4 || test_start_ips[1] != test_start_ips[2] || test_start_ips[0] == test_start_ips[1] || test_start_ips[2] == test_start_ips[3] ) ) { fprintf(stderr, "FAILURE: start IPs incorrect\n"); return -1; } if (testcase && ( strcmp(test_names[0], "a") || strcmp(test_names[1], "b") || strcmp(test_names[2], "b") || strcmp(test_names[3], "main") ) ) { fprintf(stderr, "FAILURE: procedure names are missing/incorrect\n"); return -1; } _UCD_destroy(ui); unw_destroy_addr_space(as); return 0; }
static int read_elfnotes(char *corefilename, struct UCD_info *ui) { FILE *fp = NULL; struct stat stbuf; elf64_header header; elf64_phdr *program_headers = NULL; unsigned char *notes = NULL; int i; size_t ret; elfnote *note; unsigned char *end=NULL, *curr=NULL; unsigned char *filenames = NULL, *descend = NULL , *previous_filenames = NULL; uint64_t count; int retval = -1; if (stat(corefilename, &stbuf) < 0 ) { goto func_exit; } if (!S_ISREG(stbuf.st_mode)) { goto func_exit; } fp = fopen(corefilename, "r"); if (fp == NULL) { goto func_exit; } memset(&header, 0, sizeof(header)); if (fread(&header, sizeof(header), 1, fp)!= 1) { goto func_exit; } if (header.ident[0] !=0x7f ||header.ident[1]!='E' ||header.ident[2]!='L' ||header.ident[3] !='F') { goto func_exit; } if ((header.ident[5] == 2) || (header.ident[4] != 2) || (header.phnum == 0 ) || (header.phentsize != sizeof(elf64_phdr))) { goto func_exit; } program_headers = malloc(sizeof(elf64_phdr)*header.phnum); if (program_headers == NULL) { goto func_exit; } if (fseek(fp,(long)header.phoff,SEEK_SET) == -1) { goto func_exit; } if (fread(program_headers, sizeof(elf64_phdr), header.phnum, fp) != header.phnum) { goto func_exit; } for (i=0; i< header.phnum; i++) { if (program_headers[i].type != 4) continue; retval = fseek(fp,(long) program_headers[i].offset, SEEK_SET); if (retval == -1) { goto func_exit; } notes = malloc(program_headers[i].filesz); if (notes == NULL) { goto func_exit; } ret = fread(notes, 1, program_headers[i].filesz, fp); if (ret != program_headers[i].filesz ) { goto func_exit; } end = notes + program_headers[i].filesz; curr = notes; while (curr < end) { unsigned char *descdata; note = (elfnote*) curr; descdata = (unsigned char *) note->name + addr_align (note->namesz); if (note->type != NT_FILE) { curr = (unsigned char *) (descdata + addr_align (note->descsz)); continue; } if (note->descsz < 16) goto func_exit; descend = descdata + note->descsz; if (descdata[note->descsz - 1] != '\0') goto func_exit; count = *((uint64_t *)descdata); descdata += 8; descdata += 8; if (note->descsz < 16 + count * 24 ) goto func_exit; filenames = descdata + count * 3 * 8; while (count-- > 0) { uint64_t start; if (filenames == descend) goto func_exit; start = *((uint64_t*)descdata); descdata += 8; descdata += 8; descdata += 8; if (filenames && previous_filenames && !strcmp((char*)filenames,(char*)previous_filenames)) { previous_filenames = filenames; filenames += 1 + strlen ((char *) filenames); continue; } if (_UCD_add_backing_file_at_vaddr(ui, (long)start, (char *)filenames) < 0) { // fprintf(stderr, "_UCD_add_backing_file_at_vaddr FAILED %" PRId64 " %s\n", start, filenames); } previous_filenames = filenames; filenames += 1 + strlen ((char *) filenames); } curr = (unsigned char *) (descdata + addr_align (note->descsz)); } if (notes) free(notes); notes = NULL; } retval = 0; func_exit: if (fp) fclose(fp); if (program_headers) free(program_headers); if (notes) free(notes); return(retval); }
struct sr_core_stacktrace * sr_parse_coredump(const char *core_file, const char *exe_file, char **error_msg) { struct sr_core_stacktrace *stacktrace = NULL; /* Initialize error_msg to 'no error'. */ if (error_msg) *error_msg = NULL; struct core_handle *ch = open_coredump(core_file, exe_file, error_msg); if (*error_msg) return NULL; unw_addr_space_t as; struct UCD_info *ui; as = unw_create_addr_space(&_UCD_accessors, 0); if (!as) { set_error("Failed to create address space"); goto fail_destroy_handle; } ui = _UCD_create(core_file); if (!ui) { set_error("Failed to set up core dump accessors for '%s'", core_file); goto fail_destroy_as; } struct exe_mapping_data *s; for (s = ch->segments; s != NULL; s = s->next) { if (_UCD_add_backing_file_at_vaddr(ui, s->start, s->filename) < 0) { /* Sometimes produces: * >_UCD_add_backing_file_at_segment: * Error reading from '/usr/lib/modules/3.6.9-2.fc17.x86_64/vdso/vdso.so' * Ignore errors for now & fail later. */ warn("Can't add backing file '%s' at addr 0x%jx", s->filename, (uintmax_t)s->start); /* goto fail_destroy_ui; */ } } stacktrace = sr_core_stacktrace_new(); int tnum, nthreads = _UCD_get_num_threads(ui); for (tnum = 0; tnum < nthreads; ++tnum) { struct sr_core_thread *trace = unwind_thread(ui, as, ch->dwfl, tnum, error_msg); if (trace) { stacktrace->threads = sr_core_thread_append(stacktrace->threads, trace); } else { sr_core_stacktrace_free(stacktrace); stacktrace = NULL; break; } } stacktrace->executable = realpath(exe_file, NULL); stacktrace->signal = get_signal_number_libunwind(ui); /* FIXME: is this the best we can do? */ stacktrace->crash_thread = stacktrace->threads; _UCD_destroy(ui); fail_destroy_as: unw_destroy_addr_space(as); fail_destroy_handle: core_handle_free(ch); return stacktrace; }