struct sr_operating_system * sr_operating_system_new() { struct sr_operating_system *operating_system = sr_malloc(sizeof(struct sr_operating_system)); sr_operating_system_init(operating_system); return operating_system; }
struct sr_core_stacktrace * sr_core_stacktrace_new() { struct sr_core_stacktrace *stacktrace = sr_malloc(sizeof(struct sr_core_stacktrace)); sr_core_stacktrace_init(stacktrace); return stacktrace; }
struct sr_ruby_frame * sr_ruby_frame_new() { struct sr_ruby_frame *frame = sr_malloc(sizeof(struct sr_ruby_frame)); sr_ruby_frame_init(frame); return frame; }
struct sr_java_frame * sr_java_frame_new_exception() { struct sr_java_frame *frame = sr_malloc(sizeof(*frame)); sr_java_frame_init(frame); frame->is_exception = true; return frame; }
char * sr_disasm_binary_to_text(struct sr_disasm_state *state, uint64_t start_offset, uint64_t size, char **error_message) { #if HAVE_LIBOPCODES asection *section = state->info.section; if (start_offset < section->vma || (start_offset + size) > section->vma + section->size) { *error_message = sr_asprintf( "Invalid function range: 0x%"PRIx64" - 0x%"PRIx64, start_offset, start_offset + size); return NULL; } char *code = sr_malloc(size); bool success = bfd_get_section_contents(state->bfd_file, state->info.section, code, start_offset - section->vma, size); if (!success) { *error_message = sr_strdup("Failed to get section contents."); return NULL; } struct sr_strbuf *strbuf = sr_strbuf_new(); for (int i = 0; i < size; ++i) { sr_strbuf_append_strf(strbuf, "0x%02x ", (unsigned)code[i]); if ((i + 1) % 12 == 0) sr_strbuf_append_char(strbuf, '\n'); } free(code); return sr_strbuf_free_nobuf(strbuf); #else // HAVE_LIBOPCODES *error_message = sr_asprintf("satyr compiled without libopcodes"); return NULL; #endif // HAVE_LIBOPCODES }
struct sr_distances * sr_distances_new(int m, int n) { struct sr_distances *distances = sr_malloc(sizeof(struct sr_distances)); /* The number of rows has to be smaller than columns. */ if (m >= n) m = n - 1; assert(m > 0 && n > 1 && m < n); distances->m = m; distances->n = n; distances->distances = sr_malloc_array( get_distance_position(distances, m - 1, n - 1) + 1, sizeof(*distances->distances) ); return distances; }
int so_scheduler_add(soscheduler *s , void *db) { sr_mutexlock(&s->lock); so *e = s->env; int count = s->count + 1; void **i = sr_malloc(&e->a, count * sizeof(void*)); if (srunlikely(i == NULL)) { sr_mutexunlock(&s->lock); return -1; } memcpy(i, s->i, s->count * sizeof(void*)); i[s->count] = db; void *iprev = s->i; s->i = i; s->count = count; sr_mutexunlock(&s->lock); if (iprev) sr_free(&e->a, iprev); return 0; }
int so_scheduler_del(soscheduler *s, void *db) { if (srunlikely(s->i == NULL)) return 0; sr_mutexlock(&s->lock); so *e = s->env; int count = s->count - 1; if (srunlikely(count == 0)) { s->count = 0; sr_free(&e->a, s->i); s->i = NULL; sr_mutexunlock(&s->lock); return 0; } void **i = sr_malloc(&e->a, count * sizeof(void*)); if (srunlikely(i == NULL)) { sr_mutexunlock(&s->lock); return -1; } int j = 0; int k = 0; while (j < s->count) { if (s->i[j] == db) { j++; continue; } i[k] = s->i[j]; k++; j++; } void *iprev = s->i; s->i = i; s->count = count; if (srunlikely(s->rr >= s->count)) s->rr = 0; sr_mutexunlock(&s->lock); sr_free(&e->a, iprev); return 0; }
sinode *si_nodenew(sr *r) { sinode *n = (sinode*)sr_malloc(r->a, sizeof(sinode)); if (srunlikely(n == NULL)) { sr_malfunction(r->e, "%s", "memory allocation failed"); return NULL; } n->recover = 0; n->backup = 0; n->flags = 0; n->update_time = 0; n->used = 0; si_branchinit(&n->self); n->branch = NULL; n->branch_count = 0; sr_fileinit(&n->file, r->a); sv_indexinit(&n->i0); sv_indexinit(&n->i1); sr_rbinitnode(&n->node); sr_rqinitnode(&n->nodecompact); sr_rqinitnode(&n->nodebranch); sr_listinit(&n->commit); return n; }
struct sr_elf_fde * sr_elf_get_eh_frame(const char *filename, char **error_message) { #ifdef WITH_ELFUTILS /* Open the input file. */ int fd = open(filename, O_RDONLY); if (fd < 0) { *error_message = sr_asprintf("Failed to open file %s: %s", filename, strerror(errno)); return NULL; } /* Initialize libelf on the opened file. */ Elf *elf = elf_begin(fd, ELF_C_READ, NULL); if (!elf) { *error_message = sr_asprintf("Failed to run elf_begin on file %s: %s", filename, elf_errmsg(-1)); close(fd); return NULL; } unsigned char *e_ident = (unsigned char *)elf_getident(elf, NULL); if (!e_ident) { *error_message = sr_asprintf("elf_getident failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Look up the .eh_frame section */ GElf_Shdr shdr; Elf_Data *section_data; char *find_section_error_message; if (!find_elf_section_by_name(elf, ".eh_frame", §ion_data, &shdr, &find_section_error_message)) { *error_message = sr_asprintf("Failed to find .eh_frame section for %s: %s", filename, find_section_error_message); free(find_section_error_message); elf_end(elf); close(fd); return NULL; } /* Get the address at which the executable segment is loaded. If * the .eh_frame addresses are absolute, this is used to convert * them to relative to the beginning of executable segment. We * are looking for the first LOAD segment that is executable, I * hope this is sufficient. */ size_t phnum; if (elf_getphdrnum(elf, &phnum) != 0) { *error_message = sr_asprintf("elf_getphdrnum failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } uint64_t exec_base = -1; int i; for (i = 0; i < phnum; i++) { GElf_Phdr phdr; if (gelf_getphdr(elf, i, &phdr) != &phdr) { *error_message = sr_asprintf("gelf_getphdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } if (phdr.p_type == PT_LOAD && phdr.p_flags & PF_X) { exec_base = phdr.p_vaddr; break; } } if (-1 == exec_base) { *error_message = sr_asprintf("Can't determine executable base for %s", filename); elf_end(elf); close(fd); return NULL; } /* We now have a handle to .eh_frame data. We'll use * dwarf_next_cfi to iterate through all FDEs looking for those * matching the addresses we have. * * Some info on .eh_frame can be found at * http://www.airs.com/blog/archives/460 and in DWARF * documentation for .debug_frame. The initial_location and * address_range decoding is 'inspired' by elfutils source. * * @todo If this linear scan is too slow, we can do binary search * on .eh_frame_hdr -- see http://www.airs.com/blog/archives/462 */ struct sr_elf_fde *result = NULL, *last = NULL; struct cie *cie_list = NULL, *cie_list_last = NULL; Dwarf_Off cfi_offset_next = 0; while (true) { Dwarf_CFI_Entry cfi; Dwarf_Off cfi_offset = cfi_offset_next; int ret = dwarf_next_cfi(e_ident, section_data, 1, cfi_offset, &cfi_offset_next, &cfi); if (ret > 0) { /* We're at the end. */ break; } if (ret < 0) { /* Error. If cfi_offset_next was updated, we may skip the * erroneous cfi. */ if (cfi_offset_next > cfi_offset) continue; *error_message = sr_asprintf("dwarf_next_cfi failed for %s: %s", filename, dwarf_errmsg(-1)); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } if (dwarf_cfi_cie_p(&cfi)) { /* Current CFI is a CIE. We store its offset and FDE encoding * attributes to be used when reading FDEs. */ /* Default FDE encoding (i.e. no R in augmentation string) is * DW_EH_PE_absptr. */ char *cie_error_message; struct cie *cie = read_cie(&cfi, cfi_offset, e_ident, &cie_error_message); if (!cie) { *error_message = sr_asprintf("CIE reading failed for %s: %s", filename, cie_error_message); free(cie_error_message); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } /* Append the newly parsed CIE to our list of CIEs. */ if (cie_list) { cie_list_last->next = cie; cie_list_last = cie; } else cie_list = cie_list_last = cie; } else { /* Current CFI is an FDE. */ struct cie *cie = cie_list; /* Find the CIE data that we should have saved earlier. */ while (cie) { /* In .eh_frame, CIE_pointer is relative, but libdw converts it * to absolute offset. */ if (cfi.fde.CIE_pointer == cie->cie_offset) break; /* Found. */ cie = cie->next; } if (!cie) { *error_message = sr_asprintf("CIE not found for FDE %jx in %s", (uintmax_t)cfi_offset, filename); cie_free(cie_list); sr_elf_eh_frame_free(result); elf_end(elf); close(fd); return NULL; } /* Read the two numbers we need and if they are PC-relative, * compute the offset from VMA base */ uint64_t initial_location = fde_read_address(cfi.fde.start, cie->ptr_len); uint64_t address_range = fde_read_address(cfi.fde.start + cie->ptr_len, cie->ptr_len); if (cie->pcrel) { /* We need to determine how long is the 'length' (and * consequently CIE id) field of this FDE -- it can be * either 4 or 12 bytes long. */ uint64_t length = fde_read_address(section_data->d_buf + cfi_offset, 4); uint64_t skip = (length == 0xffffffffUL ? 12 : 4); uint64_t mask = (cie->ptr_len == 4 ? 0xffffffffUL : 0xffffffffffffffffUL); initial_location += shdr.sh_offset + cfi_offset + 2 * skip; initial_location &= mask; } else { /* Assuming that not pcrel means absolute address * (what if the file is a library?). Convert to * text-section-start-relative. */ initial_location -= exec_base; } struct sr_elf_fde *fde = sr_malloc(sizeof(struct sr_elf_fde)); fde->exec_base = exec_base; fde->start_address = initial_location; fde->length = address_range; fde->next = NULL; /* Append the newly parsed Frame Description Entry to our * list of FDEs. */ if (result) { last->next = fde; last = fde; } else result = last = fde; } } cie_free(cie_list); elf_end(elf); close(fd); return result; #else /* WITH_ELFUTILS */ *error_message = sr_asprintf("satyr compiled without elfutils"); return NULL; #endif /* WITH_ELFUTILS */ }
struct sr_elf_plt_entry * sr_elf_get_procedure_linkage_table(const char *filename, char **error_message) { #ifdef WITH_ELFUTILS /* Open the input file. */ int fd = open(filename, O_RDONLY); if (fd < 0) { *error_message = sr_asprintf("Failed to open file %s: %s", filename, strerror(errno)); return NULL; } /* Initialize libelf on the opened file. */ Elf *elf = elf_begin(fd, ELF_C_READ, NULL); if (!elf) { *error_message = sr_asprintf("Failed to run elf_begin on file %s: %s", filename, elf_errmsg(-1)); close(fd); return NULL; } /* Find the .plt section. */ GElf_Shdr shdr; Elf_Data *plt_data; char *find_section_error_message; size_t plt_section_index = find_elf_section_by_name(elf, ".plt", &plt_data, &shdr, &find_section_error_message); if (0 == plt_section_index) { *error_message = sr_asprintf("Failed to find .plt section for %s: %s", filename, find_section_error_message); free(find_section_error_message); elf_end(elf); close(fd); return NULL; } /* Find the relocation section for .plt (typically .rela.plt), together * with its symbol and string table */ uint64_t plt_base = shdr.sh_addr; Elf_Data *rela_plt_data = NULL; Elf_Data *plt_symbols = NULL; size_t stringtable = 0; Elf_Scn *section = NULL; while ((section = elf_nextscn(elf, section)) != NULL) { if (gelf_getshdr(section, &shdr) != &shdr) { *error_message = sr_asprintf("gelf_getshdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } if (shdr.sh_type == SHT_RELA && shdr.sh_info == plt_section_index) { rela_plt_data = elf_getdata(section, NULL); if (!rela_plt_data) { *error_message = sr_asprintf("elf_getdata failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Get symbol section for .rela.plt */ Elf_Scn *symbol_section = elf_getscn(elf, shdr.sh_link); if (!symbol_section) { *error_message = sr_asprintf("elf_getscn failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } plt_symbols = elf_getdata(symbol_section, NULL); if (!plt_symbols) { *error_message = sr_asprintf("elf_getdata failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } /* Get string table for the symbol table. */ if (gelf_getshdr(symbol_section, &shdr) != &shdr) { *error_message = sr_asprintf("gelf_getshdr failed for %s: %s", filename, elf_errmsg(-1)); elf_end(elf); close(fd); return NULL; } stringtable = shdr.sh_link; break; } } if (0 == stringtable) { *error_message = sr_asprintf("Unable to read symbol table for .plt for file %s", filename); elf_end(elf); close(fd); return NULL; } /* PLT looks like this (see also AMD64 ABI, page 78): * * Disassembly of section .plt: * * 0000003463e01010 <attr_removef@plt-0x10>: * 3463e01010: ff 35 2a 2c 20 00 pushq 0x202c2a(%rip) <-- here is plt_base * 3463e01016: ff 25 2c 2c 20 00 jmpq *0x202c2c(%rip) each "slot" is 16B wide * 3463e0101c: 0f 1f 40 00 nopl 0x0(%rax) 0-th slot is skipped * * 0000003463e01020 <attr_removef@plt>: * 3463e01020: ff 25 2a 2c 20 00 jmpq *0x202c2a(%rip) * 3463e01026: 68 00 00 00 00 pushq $0x0 <-- this is the number we want * 3463e0102b: e9 e0 ff ff ff jmpq 3463e01010 <_init+0x18> * * 0000003463e01030 <fgetxattr@plt>: * 3463e01030: ff 25 22 2c 20 00 jmpq *0x202c22(%rip) * 3463e01036: 68 01 00 00 00 pushq $0x1 * 3463e0103b: e9 d0 ff ff ff jmpq 3463e01010 <_init+0x18> */ struct sr_elf_plt_entry *result = NULL, *last = NULL; for (unsigned plt_offset = 16; plt_offset < plt_data->d_size; plt_offset += 16) { uint32_t *plt_index = (uint32_t*)(plt_data->d_buf + plt_offset + 7); GElf_Rela rela; if (gelf_getrela(rela_plt_data, *plt_index, &rela) != &rela) { *error_message = sr_asprintf("gelf_getrela failed for %s: %s", filename, elf_errmsg(-1)); sr_elf_procedure_linkage_table_free(result); elf_end(elf); close(fd); return NULL; } GElf_Sym symb; if (gelf_getsym(plt_symbols, GELF_R_SYM(rela.r_info), &symb) != &symb) { *error_message = sr_asprintf("gelf_getsym failed for %s: %s", filename, elf_errmsg(-1)); sr_elf_procedure_linkage_table_free(result); elf_end(elf); close(fd); return NULL; } struct sr_elf_plt_entry *entry = sr_malloc(sizeof(struct sr_elf_plt_entry)); entry->symbol_name = sr_strdup(elf_strptr(elf, stringtable, symb.st_name)); entry->address = (uint64_t)(plt_base + plt_offset); entry->next = NULL; if (result) { last->next = entry; last = entry; } else result = last = entry; } elf_end(elf); close(fd); return result; #else /* WITH_ELFUTILS */ *error_message = sr_asprintf("satyr compiled without elfutils"); return NULL; #endif /* WITH_ELFUTILS */ }
struct sr_disasm_state * sr_disasm_init(const char *file_name, char **error_message) { #if HAVE_LIBOPCODES struct sr_disasm_state *state = sr_malloc(sizeof(struct sr_disasm_state)); state->bfd_file = bfd_openr(file_name, NULL); if (!state->bfd_file) { *error_message = sr_asprintf("Failed to open file %s: %s", file_name, bfd_errmsg(bfd_get_error())); free(state); return NULL; } if (!bfd_check_format(state->bfd_file, bfd_object)) { *error_message = sr_asprintf("Invalid file format of %s: %s", file_name, bfd_errmsg(bfd_get_error())); bfd_close(state->bfd_file); free(state); return NULL; } asection *section = bfd_get_section_by_name(state->bfd_file, ".text"); if (!section) { *error_message = sr_asprintf( "Failed to find .text section in %s: %s", file_name, bfd_errmsg(bfd_get_error())); bfd_close(state->bfd_file); free(state); return NULL; } state->disassembler = disassembler(state->bfd_file); if (!state->disassembler) { *error_message = sr_asprintf( "Unable to find disassembler for %s", file_name); bfd_close(state->bfd_file); free(state); return NULL; } init_disassemble_info(&state->info, NULL, buffer_printf); state->info.arch = bfd_get_arch(state->bfd_file); state->info.mach = bfd_get_mach(state->bfd_file); state->info.buffer_vma = section->vma; state->info.buffer_length = section->size; state->info.section = section; /* TODO: memory error func */ bfd_malloc_and_get_section(state->bfd_file, section, &state->info.buffer); disassemble_init_for_target(&state->info); return state; #else // HAVE_LIBOPCODES *error_message = sr_asprintf("satyr compiled without libopcodes"); return NULL; #endif // HAVE_LIBOPCODES }
static struct sr_core_thread * unwind_thread(struct UCD_info *ui, unw_addr_space_t as, Dwfl *dwfl, int thread_no, char **error_msg) { int ret; unw_cursor_t c; struct sr_core_frame *trace = NULL; _UCD_select_thread(ui, thread_no); ret = unw_init_remote(&c, as, ui); if (ret < 0) { set_error("unw_init_remote failed: %s", unw_strerror(ret)); return NULL; } int count = 1000; while (--count > 0) { unw_word_t ip; ret = unw_get_reg(&c, UNW_REG_IP, &ip); if (ret < 0) warn("unw_get_reg(UNW_REG_IP) failed: %s", unw_strerror(ret)); /* Seen this happen when unwinding thread that did not start * in main(). */ if (ip == 0) break; struct sr_core_frame *entry = resolve_frame(dwfl, ip, false); if (!entry->function_name) { size_t funcname_len = 512; char *funcname = sr_malloc(funcname_len); if (unw_get_proc_name(&c, funcname, funcname_len, NULL) == 0) entry->function_name = funcname; else free(funcname); } trace = sr_core_frame_append(trace, entry); /* printf("%s 0x%llx %s %s -\n", (ip_seg && ip_seg->build_id) ? ip_seg->build_id : "-", (unsigned long long)(ip_seg ? ip - ip_seg->vaddr : ip), (entry->symbol ? entry->symbol : "-"), (ip_seg && ip_seg->filename) ? ip_seg->filename : "-"); */ /* Do not unwind below __libc_start_main. */ if (0 == sr_strcmp0(entry->function_name, "__libc_start_main")) break; ret = unw_step(&c); if (ret == 0) break; if (ret < 0) { warn("unw_step failed: %s", unw_strerror(ret)); break; } } if (error_msg && !*error_msg && !trace) { set_error("No frames found for thread %d", thread_no); } struct sr_core_thread *thread = sr_core_thread_new(); thread->frames = trace; return thread; }