static u64 get_address_of_subroutine(struct mi_process proc, char *sr) { for (int i = 0; i < proc.debug.func_count; ++i) { struct mi_function *func = proc.debug.functions + i; if (0 == strcmp(func->name, sr)) { return(func->offset); } } return(0); } static struct mi_sourcepoint * pc_to_sourcepoint(struct mi_process proc, u64 pc) { // NOTE: find first point BIGGER that pc, return the sourcepoint just before that // TODO: binary search for (int i = 0; i < proc.debug.sp_count; ++i) { struct mi_sourcepoint *point = proc.debug.sp_table + i; if (point->pc > pc) { return(proc.debug.sp_table + i - 1); } } return(0); } static void print_word_at_address(int child, u64 address) { long word = ptrace(PTRACE_PEEKDATA, child, address, NULL); u8 nb[4]; memcpy(nb, &word, 4); printf("word at %#018lx: {%x %x %x %x}\n", address, nb[0], nb[1], nb[2], nb[3]); } static struct mi_buffer read_file_mmap(char *path) { struct mi_buffer result = { 0 }; struct stat s; int fd = open(path, O_RDONLY); assert(fd != -1); fstat(fd, &s); result.data = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); result.size = s.st_size; printf("mmap()-ed %ld bytes (%s)\n", result.size, path); close(fd); return(result); } static void print_sourcepoint(struct mi_process proc, struct mi_sourcepoint *sp) { // NOTE: sourcepoint file indices are 1-based if (proc.source_files[sp->file - 1].data == 0) { char *filename = proc.source_file_names[sp->file - 1]; char *dir = proc.debug.comp_dir; char full_path[512] = { 0 }; snprintf(full_path, 511, "%s/%s", dir, filename); struct mi_buffer file = read_file_mmap(full_path); proc.source_files[sp->file - 1] = file; } struct mi_buffer file = proc.source_files[sp->file - 1]; char *source = (char *) file.data; u64 size = file.size; int current_line = 1; for (u32 i = 0; i < size; ++i) { if (current_line == sp->line) { int len = 0; int start = 0; int leading = 1; for (u32 j = i; j < size; ++j) { if (leading && (source[j] == '\t' || source[j] == ' ')) { ++start; } else { leading = 0; } if (source[j] == '\n') { break; } ++len; } printf("%.*s\n", len - start, source + i + start); return; } if (source[i] == '\n') { current_line++; } } } static struct mi_process process_create(char *path) { struct mi_process result = { 0 }; /* disable ASLR */ syscall(SYS_personality, ADDR_NO_RANDOMIZE); pid_t pid = fork(); if (pid == -1) { DIE("fork error\n"); } if (pid == 0) { int rt = ptrace(PTRACE_TRACEME, 0, 0, 0); assert(rt == 0); char *args[] = { path, NULL }; rt = execvp(path, args); if (rt == -1) { DIE("exevp error\n"); } } struct mi_buffer file = read_file_mmap(path); result.elf = file.data; result.elf_size = file.size; result.pid = pid; return(result); } static struct mi_registers get_process_registers(struct mi_process proc) { struct user_regs_struct regs = { 0 }; struct iovec iov = { ®s, sizeof(regs) }; struct mi_registers result = { 0 }; ptrace(PTRACE_GETREGSET, proc.pid, NT_PRSTATUS, &iov); result.rax = regs.rax; result.rip = regs.rip; result.rbp = regs.rbp; result.rbx = regs.rbx; result.rcx = regs.rcx; result.rdx = regs.rdx; result.rsi = regs.rsi; result.rdi = regs.rdi; result.rsp = regs.rsp; result._sys = regs; return(result); } static void set_process_registers(struct mi_process proc, struct mi_registers regs) { struct iovec iov = { ®s._sys, sizeof(regs._sys) }; ptrace(PTRACE_GETREGSET, proc.pid, NT_PRSTATUS, &iov); } static void print_current_instruction(struct mi_process proc) { struct mi_registers regs = get_process_registers(proc); long wordb = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip - 4, NULL); long word = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip, NULL); long worda = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip + 4, NULL); u8 nb[12]; memcpy(nb, &wordb, 4); memcpy(nb + 4, &word, 4); memcpy(nb + 8, &worda, 4); printf("PC = %#018lx: %x %x %x %x [%x] %x %x %x %x %x %x %x\n", regs.rip, nb[0], nb[1], nb[2], nb[3], nb[4], nb[5], nb[6], nb[7], nb[8], nb[9], nb[10], nb[11]); }