struct UCD_info * _UCD_create(const char *filename) { union { Elf32_Ehdr h32; Elf64_Ehdr h64; } elf_header; #define elf_header32 elf_header.h32 #define elf_header64 elf_header.h64 bool _64bits; struct UCD_info *ui = memset(malloc(sizeof(*ui)), 0, sizeof(*ui)); ui->edi.di_cache.format = -1; ui->edi.di_debug.format = -1; #if UNW_TARGET_IA64 ui->edi.ktab.format = -1; #endif int fd = ui->coredump_fd = open(filename, O_RDONLY); if (fd < 0) goto err; ui->coredump_filename = strdup(filename); /* No sane ELF32 file is going to be smaller then ELF64 _header_, * so let's just read 64-bit sized one. */ if (read(fd, &elf_header64, sizeof(elf_header64)) != sizeof(elf_header64)) { Debug(0, "'%s' is not an ELF file\n", filename); goto err; } if (memcmp(&elf_header32, ELFMAG, SELFMAG) != 0) { Debug(0, "'%s' is not an ELF file\n", filename); goto err; } if (elf_header32.e_ident[EI_CLASS] != ELFCLASS32 && elf_header32.e_ident[EI_CLASS] != ELFCLASS64) { Debug(0, "'%s' is not a 32/64 bit ELF file\n", filename); goto err; } if (WE_ARE_LITTLE_ENDIAN != (elf_header32.e_ident[EI_DATA] == ELFDATA2LSB)) { Debug(0, "'%s' is endian-incompatible\n", filename); goto err; } _64bits = (elf_header32.e_ident[EI_CLASS] == ELFCLASS64); if (_64bits && sizeof(elf_header64.e_entry) > sizeof(off_t)) { Debug(0, "Can't process '%s': 64-bit file " "while only %ld bits are supported", filename, 8L * sizeof(off_t)); goto err; } /* paranoia check */ if (_64bits ? 0 /* todo: (elf_header64.e_ehsize != NN || elf_header64.e_phentsize != NN) */ : (elf_header32.e_ehsize != 52 || elf_header32.e_phentsize != 32) ) { Debug(0, "'%s' has wrong e_ehsize or e_phentsize\n", filename); goto err; } off_t ofs = (_64bits ? elf_header64.e_phoff : elf_header32.e_phoff); if (lseek(fd, ofs, SEEK_SET) != ofs) { Debug(0, "Can't read phdrs from '%s'\n", filename); goto err; } unsigned size = ui->phdrs_count = (_64bits ? elf_header64.e_phnum : elf_header32.e_phnum); coredump_phdr_t *phdrs = ui->phdrs = memset(malloc(size * sizeof(phdrs[0])), 0, size * sizeof(phdrs[0])); if (_64bits) { coredump_phdr_t *cur = phdrs; unsigned i = 0; while (i < size) { Elf64_Phdr hdr64; if (read(fd, &hdr64, sizeof(hdr64)) != sizeof(hdr64)) { Debug(0, "Can't read phdrs from '%s'\n", filename); goto err; } cur->p_type = hdr64.p_type ; cur->p_flags = hdr64.p_flags ; cur->p_offset = hdr64.p_offset; cur->p_vaddr = hdr64.p_vaddr ; /*cur->p_paddr = hdr32.p_paddr ; always 0 */ //TODO: check that and abort if it isn't? cur->p_filesz = hdr64.p_filesz; cur->p_memsz = hdr64.p_memsz ; cur->p_align = hdr64.p_align ; /* cur->backing_filename = NULL; - done by memset */ cur->backing_fd = -1; cur->backing_filesize = hdr64.p_filesz; i++; cur++; } } else { coredump_phdr_t *cur = phdrs; unsigned i = 0; while (i < size) { Elf32_Phdr hdr32; if (read(fd, &hdr32, sizeof(hdr32)) != sizeof(hdr32)) { Debug(0, "Can't read phdrs from '%s'\n", filename); goto err; } cur->p_type = hdr32.p_type ; cur->p_flags = hdr32.p_flags ; cur->p_offset = hdr32.p_offset; cur->p_vaddr = hdr32.p_vaddr ; /*cur->p_paddr = hdr32.p_paddr ; always 0 */ cur->p_filesz = hdr32.p_filesz; cur->p_memsz = hdr32.p_memsz ; cur->p_align = hdr32.p_align ; /* cur->backing_filename = NULL; - done by memset */ cur->backing_fd = -1; cur->backing_filesize = hdr32.p_memsz; i++; cur++; } } unsigned i = 0; coredump_phdr_t *cur = phdrs; while (i < size) { Debug(2, "phdr[%03d]: type:%d", i, cur->p_type); if (cur->p_type == PT_NOTE) { Elf32_Nhdr *note_hdr, *note_end; unsigned n_threads; ui->note_phdr = malloc(cur->p_filesz); if (lseek(fd, cur->p_offset, SEEK_SET) != (off_t)cur->p_offset || (uoff_t)read(fd, ui->note_phdr, cur->p_filesz) != cur->p_filesz) { Debug(0, "Can't read PT_NOTE from '%s'\n", filename); goto err; } note_end = STRUCT_MEMBER_P (ui->note_phdr, cur->p_filesz); /* Count number of threads */ n_threads = 0; note_hdr = (Elf32_Nhdr *)ui->note_phdr; while (NOTE_FITS (note_hdr, note_end)) { if (note_hdr->n_type == NT_PRSTATUS) n_threads++; note_hdr = NOTE_NEXT (note_hdr); } ui->n_threads = n_threads; ui->threads = malloc(sizeof (void *) * n_threads); n_threads = 0; note_hdr = (Elf32_Nhdr *)ui->note_phdr; while (NOTE_FITS (note_hdr, note_end)) { if (note_hdr->n_type == NT_PRSTATUS) ui->threads[n_threads++] = NOTE_DATA (note_hdr); note_hdr = NOTE_NEXT (note_hdr); } } if (cur->p_type == PT_LOAD) { Debug(2, " ofs:%08llx va:%08llx filesize:%08llx memsize:%08llx flg:%x", (unsigned long long) cur->p_offset, (unsigned long long) cur->p_vaddr, (unsigned long long) cur->p_filesz, (unsigned long long) cur->p_memsz, cur->p_flags ); if (cur->p_filesz < cur->p_memsz) Debug(2, " partial"); if (cur->p_flags & PF_X) Debug(2, " executable"); } Debug(2, "\n"); i++; cur++; } if (ui->n_threads == 0) { Debug(0, "No NT_PRSTATUS note found in '%s'\n", filename); goto err; } ui->prstatus = ui->threads[0]; return ui; err: _UCD_destroy(ui); return NULL; }
int main(int argc , char **argv) { unw_addr_space_t as; unw_cursor_t cursor; struct UCD_info *ui; unw_word_t ip, sp, off; char buf[512], name[256]; int line; int depth = 0; int ret; bool pybt_done = false; #define TEST_NAME_LEN 256 install_signal_handler(); if (argc != 3) { fprintf(stderr, "Usage: %s <binary> <corefile>", argv[0]); exit(1); } as = unw_create_addr_space(&_UCD_accessors, 0); if (!as) { fprintf(stderr, "unw_create_addr_space() failed"); exit(1); } ui = _UCD_create(argv[2]); if (!ui) { fprintf(stderr,"_UCD_create('%s') failed", argv[1]); exit(1); } ret = unw_init_remote(&cursor, as, ui); if (ret < 0) { fprintf(stderr,"unw_init_remote() failed: ret=%d\n", ret); exit(1); } read_elfnotes(argv[2], ui); while (unw_step(&cursor) > 0) { // Avoid going too deep if (depth++ > MAX_STACK_DEPTH) { exit(1); } unw_get_reg(&cursor, UNW_REG_IP, &ip); unw_get_reg(&cursor, UNW_REG_SP, &sp); if (unw_get_proc_name(&cursor, name, sizeof (name), &off) == 0) { if (off) { snprintf(buf, sizeof (buf), "<%s+0x%lx>", name, (long) off); } else { snprintf(buf, sizeof (buf), "<%s>", name); } } /* Check for Python backtrace */ if (!strncmp(name,"PyEval_EvalFrameEx", 18) && !pybt_done) { pybacktrace(cursor); pybt_done = true; } rw_get_file_and_line((long)ip, name, argv[1], 256, &line); printf("%016lx %s <%s:%d> (sp=%016lx)\n", (long) ip, buf, basename(name), line, (long) sp); } _UCD_destroy(ui); unw_destroy_addr_space(as); return 0; }
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; }
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; }