// SPDX-FileCopyrightText: 2009-2019 pancake // SPDX-License-Identifier: LGPL-3.0-only #include #include #include #include #include #include #include #include #include #include "native/drx.c" // x86 specific #include "rz_cons.h" static int rz_debug_native_continue(RzDebug *dbg, int pid, int tid, int sig); static int rz_debug_native_reg_read(RzDebug *dbg, int type, ut8 *buf, int size); static int rz_debug_native_reg_write(RzDebug *dbg, int type, const ut8 *buf, int size); #include "native/bt.c" #if __UNIX__ #include #if !defined(__HAIKU__) && !defined(__sun) #include #endif #include #include #endif #if __WINDOWS__ #include "native/windows/windows_debug.h" // TODO: Move these onto windows.h? RZ_API RzList *rz_w32_dbg_modules(RzDebug *); // ugly! RZ_API RzList *rz_w32_dbg_maps(RzDebug *); #define RZ_DEBUG_REG_T CONTEXT #ifdef NTSTATUS #undef NTSTATUS #endif #ifndef NTSTATUS #define NTSTATUS int #endif #elif __BSD__ #include "native/bsd/bsd_debug.h" #include "native/procfs.h" #elif __APPLE__ #include #include "native/xnu/xnu_debug.h" #elif __sun #define RZ_DEBUG_REG_T gregset_t #undef DEBUGGER #define DEBUGGER 0 #warning No debugger support for SunOS yet #elif __linux__ #include #include "native/linux/linux_debug.h" #include "native/procfs.h" #ifdef __ANDROID__ #define WAIT_ANY -1 #ifndef WIFCONTINUED #define WIFCONTINUED(s) ((s) == 0xffff) #endif #endif #if (__x86_64__ || __i386__ || __arm__ || __arm64__) && !defined(__ANDROID__) #include "native/linux/linux_coredump.h" #endif #else // OS #warning Unsupported debugging platform #undef DEBUGGER #define DEBUGGER 0 #endif // ARCH #ifdef __WALL #define WAITPID_FLAGS __WALL #else #define WAITPID_FLAGS 0 #endif /* begin of debugger code */ #if DEBUGGER #define PROC_NAME_SZ 1024 #define PROC_REGION_SZ 100 // PROC_REGION_SZ - 2 (used for `0x`). Due to how RZ_STR_DEF works this can't be // computed. #define PROC_REGION_LEFT_SZ 98 #define PROC_PERM_SZ 5 #define PROC_UNKSTR_SZ 128 #if !__WINDOWS__ && !(__linux__ && !defined(WAIT_ON_ALL_CHILDREN)) && !__APPLE__ static int rz_debug_handle_signals(RzDebug *dbg) { #if __KFBSD__ || __NetBSD__ return bsd_handle_signals(dbg); #else eprintf("Warning: signal handling is not supported on this platform\n"); return 0; #endif } #endif // this is temporal #if __APPLE__ || __linux__ static char *rz_debug_native_reg_profile(RzDebug *dbg) { #if __APPLE__ return xnu_reg_profile(dbg); #elif __linux__ return linux_reg_profile(dbg); #endif } #else #include "native/reg.c" // x86 specific #endif static bool rz_debug_native_step(RzDebug *dbg) { #if __APPLE__ return xnu_step(dbg); #elif __WINDOWS__ return w32_step(dbg); #elif __BSD__ int ret = ptrace(PT_STEP, dbg->pid, (caddr_t)1, 0); if (ret != 0) { perror("native-singlestep"); return false; } return true; #elif __linux__ return linux_step(dbg); #else return false; #endif } // return thread id static int rz_debug_native_attach(RzDebug *dbg, int pid) { #if __APPLE__ return xnu_attach(dbg, pid); #elif __WINDOWS__ return w32_attach(dbg, pid); #elif __linux__ || __ANDROID__ return linux_attach(dbg, pid); #elif __KFBSD__ if (ptrace(PT_ATTACH, pid, 0, 0) != -1) { perror("ptrace (PT_ATTACH)"); } return pid; #else int ret = ptrace(PTRACE_ATTACH, pid, 0, 0); if (ret != -1) { eprintf("Trying to attach to %d\n", pid); perror("ptrace (PT_ATTACH)"); } return pid; #endif } static int rz_debug_native_detach(RzDebug *dbg, int pid) { #if __APPLE__ return xnu_detach(dbg, pid); #elif __WINDOWS__ return w32_detach(dbg, pid); #elif __BSD__ return ptrace(PT_DETACH, pid, NULL, 0); #else return rz_debug_ptrace(dbg, PTRACE_DETACH, pid, NULL, (rz_ptrace_data_t)(size_t)0); #endif } #if __WINDOWS__ static int rz_debug_native_select(RzDebug *dbg, int pid, int tid) { return w32_select(dbg, pid, tid); } #elif __linux__ static int rz_debug_native_select(RzDebug *dbg, int pid, int tid) { return linux_select(dbg, pid, tid); } #endif static int rz_debug_native_continue_syscall(RzDebug *dbg, int pid, int num) { // XXX: num is ignored #if __linux__ linux_set_options(dbg, pid); return rz_debug_ptrace(dbg, PTRACE_SYSCALL, pid, 0, 0); #elif __BSD__ ut64 pc = rz_debug_reg_get(dbg, "PC"); errno = 0; return ptrace(PTRACE_SYSCALL, pid, (void *)(size_t)pc, 0) == 0; #else eprintf("TODO: continue syscall not implemented yet\n"); return -1; #endif } #if !__WINDOWS__ && !__APPLE__ && !__BSD__ /* Callback to trigger SIGINT signal */ static void interrupt_process(RzDebug *dbg) { rz_debug_kill(dbg, dbg->pid, dbg->tid, SIGINT); rz_cons_break_pop(); } #endif static int rz_debug_native_stop(RzDebug *dbg) { #if __linux__ // Stop all running threads except the thread reported by waitpid return linux_stop_threads(dbg, dbg->reason.tid); #else return 0; #endif } /* TODO: specify thread? */ /* TODO: must return true/false */ static int rz_debug_native_continue(RzDebug *dbg, int pid, int tid, int sig) { #if __APPLE__ bool ret = xnu_continue(dbg, pid, tid, sig); if (!ret) { return -1; } return tid; #elif __WINDOWS__ return w32_continue(dbg, pid, tid, sig); #elif __BSD__ void *data = (void *)(size_t)((sig != -1) ? sig : dbg->reason.signum); ut64 pc = rz_debug_reg_get(dbg, "PC"); return ptrace(PTRACE_CONT, pid, (void *)(size_t)pc, (int)(size_t)data) == 0; #else int contsig = dbg->reason.signum; int ret = -1; if (sig != -1) { contsig = sig; } /* SIGINT handler for attached processes: dbg.consbreak (disabled by default) */ if (dbg->consbreak) { rz_cons_break_push((RzConsBreak)interrupt_process, dbg); } if (dbg->continue_all_threads && dbg->n_threads && dbg->threads) { RzDebugPid *th; RzListIter *it; rz_list_foreach (dbg->threads, it, th) { ret = rz_debug_ptrace(dbg, PTRACE_CONT, th->pid, 0, 0); if (ret) { eprintf("Error: (%d) is running or dead.\n", th->pid); } } } else { ret = rz_debug_ptrace(dbg, PTRACE_CONT, tid, NULL, (rz_ptrace_data_t)(size_t)contsig); if (ret) { rz_sys_perror("PTRACE_CONT"); } } // return ret >= 0 ? tid : false; return tid; #endif } static RzDebugInfo *rz_debug_native_info(RzDebug *dbg, const char *arg) { #if __APPLE__ return xnu_info(dbg, arg); #elif __WINDOWS__ return w32_info(dbg, arg); #elif __linux__ return linux_info(dbg, arg); #elif __KFBSD__ || __OpenBSD__ || __NetBSD__ return bsd_info(dbg, arg); #else return NULL; #endif } #if __WINDOWS__ static bool tracelib(RzDebug *dbg, const char *mode, PLIB_ITEM item) { const char *needle = NULL; int tmp = 0; if (mode) { switch (mode[0]) { case 'l': needle = dbg->glob_libs; break; case 'u': needle = dbg->glob_unlibs; break; } } rz_cons_printf("(%d) %sing library at 0x%p (%s) %s\n", item->pid, mode, item->BaseOfDll, item->Path, item->Name); rz_cons_flush(); if (needle && strlen(needle)) { tmp = rz_str_glob(item->Name, needle); } return !mode || !needle || tmp; } #endif /* * Wait for an event and start trying to figure out what to do with it. * * Returns RZ_DEBUG_REASON_* */ #if __WINDOWS__ static RzDebugReasonType rz_debug_native_wait(RzDebug *dbg, int pid) { RzDebugReasonType reason = RZ_DEBUG_REASON_UNKNOWN; // Store the original TID to attempt to switch back after handling events that // require switching to the event's thread that shouldn't bother the user int orig_tid = dbg->tid; bool restore_thread = false; W32DbgWInst *wrap = dbg->plugin_data; if (pid == -1) { RZ_LOG_ERROR("rz_debug_native_wait called with pid -1\n"); return RZ_DEBUG_REASON_ERROR; } reason = w32_dbg_wait(dbg, pid); RzDebugInfo *r = rz_debug_native_info(dbg, ""); if (reason == RZ_DEBUG_REASON_NEW_LIB) { if (r && r->lib) { if (tracelib(dbg, "load", r->lib)) { reason = RZ_DEBUG_REASON_TRAP; } /* Check if autoload PDB is set, and load PDB information if yes */ RzCore *core = dbg->corebind.core; bool autoload_pdb = dbg->corebind.cfggeti(core, "pdb.autoload"); if (autoload_pdb) { PLIB_ITEM lib = r->lib; RzBinOptions opts = { 0 }; opts.obj_opts.baseaddr = (uintptr_t)lib->BaseOfDll; RzBinFile *cur = rz_bin_cur(core->bin); RzBinFile *bf = rz_bin_open(core->bin, lib->Path, &opts); if (bf) { const RzBinInfo *info = rz_bin_object_get_info(bf->o); if (RZ_STR_ISNOTEMPTY(info->debug_file_name)) { if (!rz_file_exists(info->debug_file_name)) { dbg->corebind.cmdf(core, "idpd"); } dbg->corebind.cmd(core, "idp"); } rz_bin_file_set_cur_binfile(core->bin, cur); } } } else { RZ_LOG_WARN("Loading unknown library.\n"); } restore_thread = true; } else if (reason == RZ_DEBUG_REASON_EXIT_LIB) { RzDebugInfo *r = rz_debug_native_info(dbg, ""); if (r && r->lib) { if (tracelib(dbg, "unload", r->lib)) { reason = RZ_DEBUG_REASON_TRAP; } } else { RZ_LOG_WARN("Unloading unknown library.\n"); } restore_thread = true; } else if (reason == RZ_DEBUG_REASON_NEW_PID) { if (r && r->thread) { PTHREAD_ITEM item = r->thread; RZ_LOG_INFO("(%d) Created process %d (start @ %p) (teb @ %p)\n", item->pid, item->tid, item->lpStartAddress, item->lpThreadLocalBase); } } else if (reason == RZ_DEBUG_REASON_NEW_TID) { if (r && r->thread) { PTHREAD_ITEM item = r->thread; RZ_LOG_INFO("(%d) Created thread %d (start @ %p) (teb @ %p)\n", item->pid, item->tid, item->lpStartAddress, item->lpThreadLocalBase); } restore_thread = true; } else if (reason == RZ_DEBUG_REASON_EXIT_TID) { PTHREAD_ITEM item = r->thread; if (r && r->thread) { RZ_LOG_INFO("(%d) Finished thread %d Exit code %lu\n", (ut32)item->pid, (ut32)item->tid, item->dwExitCode); } if (dbg->tid != orig_tid && item->tid != orig_tid) { restore_thread = true; } } else if (reason == RZ_DEBUG_REASON_DEAD) { if (r && r->thread) { PTHREAD_ITEM item = r->thread; RZ_LOG_INFO("(%d) Finished process with exit code %lu\n", dbg->main_pid, item->dwExitCode); } dbg->pid = -1; dbg->tid = -1; } else if (reason == RZ_DEBUG_REASON_USERSUSP && dbg->tid != orig_tid) { if (r && r->thread) { PTHREAD_ITEM item = r->thread; RZ_LOG_INFO("(%d) Created DebugBreak thread %d (start @ %p)\n", item->pid, item->tid, item->lpStartAddress); } // DebugProcessBreak creates a new thread that will trigger a breakpoint. We record the // tid here to ignore it once the breakpoint is hit. wrap->break_tid = dbg->tid; restore_thread = true; } else if (reason == RZ_DEBUG_REASON_BREAKPOINT && dbg->tid == wrap->break_tid) { wrap->break_tid = -2; reason = RZ_DEBUG_REASON_NONE; restore_thread = true; } rz_debug_info_free(r); if (restore_thread) { // Attempt to return to the original thread after handling the event dbg->tid = w32_select(dbg, dbg->pid, orig_tid); if (dbg->tid == -1) { dbg->pid = -1; reason = RZ_DEBUG_REASON_DEAD; } else { if (dbg->tid != orig_tid) { reason = RZ_DEBUG_REASON_UNKNOWN; } } } dbg->reason.tid = pid; dbg->reason.type = reason; return reason; } // FIXME: Should WAIT_ON_ALL_CHILDREN be a compilation flag instead of runtime debug config? #elif __linux__ && !defined(WAIT_ON_ALL_CHILDREN) // __WINDOWS__ static RzDebugReasonType rz_debug_native_wait(RzDebug *dbg, int pid) { RzDebugReasonType reason = RZ_DEBUG_REASON_UNKNOWN; if (pid == -1) { eprintf("ERROR: rz_debug_native_wait called with pid -1\n"); return RZ_DEBUG_REASON_ERROR; } reason = linux_dbg_wait(dbg, dbg->tid); dbg->reason.type = reason; return reason; } #else // if __WINDOWS__ & elif __linux__ && !defined (WAIT_ON_ALL_CHILDREN) static RzDebugReasonType rz_debug_native_wait(RzDebug *dbg, int pid) { RzDebugReasonType reason = RZ_DEBUG_REASON_UNKNOWN; if (pid == -1) { eprintf("ERROR: rz_debug_native_wait called with pid -1\n"); return RZ_DEBUG_REASON_ERROR; } #if __APPLE__ rz_cons_break_push(NULL, NULL); do { reason = xnu_wait(dbg, pid); if (reason == RZ_DEBUG_REASON_MACH_RCV_INTERRUPTED) { if (rz_cons_is_breaked()) { // Perhaps check the inferior is still alive, // otherwise xnu_stop will fail. reason = xnu_stop(dbg, pid) ? RZ_DEBUG_REASON_USERSUSP : RZ_DEBUG_REASON_UNKNOWN; } else { // Weird; we'll retry the wait. continue; } } break; } while (true); rz_cons_break_pop(); #else int status = -1; // XXX: this is blocking, ^C will be ignored #ifdef WAIT_ON_ALL_CHILDREN int ret = waitpid(-1, &status, WAITPID_FLAGS); #else int ret = waitpid(-1, &status, 0); if (ret != -1) { reason = RZ_DEBUG_REASON_TRAP; } #endif // WAIT_ON_ALL_CHILDREN if (ret == -1) { rz_sys_perror("waitpid"); return RZ_DEBUG_REASON_ERROR; } // eprintf ("rz_debug_native_wait: status=%d (0x%x) (return=%d)\n", status, status, ret); #ifdef WAIT_ON_ALL_CHILDREN if (ret != pid) { reason = RZ_DEBUG_REASON_NEW_PID; eprintf("switching to pid %d\n", ret); rz_debug_select(dbg, ret, ret); } #endif // WAIT_ON_ALL_CHILDREN // TODO: switch status and handle reasons here // FIXME: Remove linux handling from this function? #if __linux__ && defined(PT_GETEVENTMSG) reason = linux_ptrace_event(dbg, pid, status, true); #endif // __linux__ /* propagate errors */ if (reason == RZ_DEBUG_REASON_ERROR) { return reason; } /* we don't know what to do yet, let's try harder to figure it out. */ #if __FreeBSD__ if (reason == RZ_DEBUG_REASON_TRAP) { #else if (reason == RZ_DEBUG_REASON_UNKNOWN) { #endif if (WIFEXITED(status)) { eprintf("child exited with status %d\n", WEXITSTATUS(status)); reason = RZ_DEBUG_REASON_DEAD; } else if (WIFSIGNALED(status)) { eprintf("child received signal %d\n", WTERMSIG(status)); reason = RZ_DEBUG_REASON_SIGNAL; } else if (WIFSTOPPED(status)) { if (WSTOPSIG(status) != SIGTRAP && WSTOPSIG(status) != SIGSTOP) { eprintf("Child stopped with signal %d\n", WSTOPSIG(status)); } /* the ptrace documentation says GETSIGINFO is only necessary for * differentiating the various stops. * * this might modify dbg->reason.signum */ #if __OpenBSD__ reason = RZ_DEBUG_REASON_BREAKPOINT; #else if (rz_debug_handle_signals(dbg) != 0) { return RZ_DEBUG_REASON_ERROR; } reason = dbg->reason.type; #endif #ifdef WIFCONTINUED } else if (WIFCONTINUED(status)) { eprintf("child continued...\n"); reason = RZ_DEBUG_REASON_NONE; #endif } else if (status == 1) { /* XXX(jjd): does this actually happen? */ eprintf("debugger is dead with status 1!\n"); reason = RZ_DEBUG_REASON_DEAD; } else if (status == 0) { /* XXX(jjd): does this actually happen? */ eprintf("debugger is dead with status 0\n"); reason = RZ_DEBUG_REASON_DEAD; } else { if (ret != pid) { reason = RZ_DEBUG_REASON_NEW_PID; } else { /* ugh. still don't know :-/ */ eprintf("returning from wait without knowing why...\n"); } } } /* if we still don't know what to do, we have a problem... */ if (reason == RZ_DEBUG_REASON_UNKNOWN) { eprintf("%s: no idea what happened...\n", __func__); reason = RZ_DEBUG_REASON_ERROR; } #endif // __APPLE__ dbg->reason.tid = pid; dbg->reason.type = reason; return reason; } #endif // __WINDOWS__ #undef MAXPID #define MAXPID 99999 static RzList /**/ *rz_debug_native_pids(RzDebug *dbg, int pid) { RzList *list = rz_list_new(); if (!list) { return NULL; } #if __APPLE__ if (pid) { RzDebugPid *p = xnu_get_pid(pid); if (p) { rz_list_append(list, p); } } else { int i; for (i = 1; i < MAXPID; i++) { RzDebugPid *p = xnu_get_pid(i); if (p) { rz_list_append(list, p); } } } #elif __WINDOWS__ return w32_pid_list(dbg, pid, list); #elif __linux__ return linux_pid_list(pid, list); #else /* rest is BSD */ return bsd_pid_list(dbg, pid, list); #endif return list; } static RzList /**/ *rz_debug_native_threads(RzDebug *dbg, int pid) { RzList *list = rz_list_new(); if (!list) { eprintf("No list?\n"); return NULL; } #if __APPLE__ return xnu_thread_list(dbg, pid, list); #elif __WINDOWS__ return w32_thread_list(dbg, pid, list); #elif __linux__ return linux_thread_list(dbg, pid, list); #else return bsd_thread_list(dbg, pid, list); #endif } #if __sun || __NetBSD__ || __KFBSD__ || __OpenBSD__ || __DragonFly__ // Function to read register from Linux, BSD, Android systems static int bsd_reg_read(RzDebug *dbg, int type, ut8 *buf, int size) { int showfpu = false; int pid = dbg->pid; int ret; if (type < -1) { showfpu = true; // hack for debugging type = -type; } switch (type) { case RZ_REG_TYPE_DRX: #if __i386__ || __x86_64__ #if __KFBSD__ { // TODO struct dbreg dbr; ret = ptrace(PT_GETDBREGS, pid, (caddr_t)&dbr, sizeof(dbr)); if (ret != 0) return false; // XXX: maybe the register map is not correct, must review } #endif #endif return true; break; case RZ_REG_TYPE_FPU: case RZ_REG_TYPE_MMX: case RZ_REG_TYPE_XMM: break; case RZ_REG_TYPE_SEG: case RZ_REG_TYPE_FLG: case RZ_REG_TYPE_GPR: { RZ_DEBUG_REG_T regs; memset(®s, 0, sizeof(regs)); memset(buf, 0, size); #if __NetBSD__ || __OpenBSD__ ret = ptrace(PTRACE_GETREGS, pid, (caddr_t)®s, sizeof(regs)); #elif __KFBSD__ ret = ptrace(PT_GETREGS, pid, (caddr_t)®s, 0); #else #warning not implemented for this platform ret = 1; #endif // if perror here says 'no such process' and the // process exists still.. is because there's a // missing call to 'wait'. and the process is not // yet available to accept more ptrace queries. if (ret != 0) return false; if (sizeof(regs) < size) size = sizeof(regs); memcpy(buf, ®s, size); return sizeof(regs); } break; } return true; } #endif // if __sun || __NetBSD__ || __KFBSD__ || __OpenBSD__ // TODO: what about float and hardware regs here ??? // TODO: add flag for type static int rz_debug_native_reg_read(RzDebug *dbg, int type, ut8 *buf, int size) { if (size < 1) { return false; } #if __APPLE__ return xnu_reg_read(dbg, type, buf, size); #elif __WINDOWS__ return w32_reg_read(dbg, type, buf, size); #elif __linux__ return linux_reg_read(dbg, type, buf, size); #elif __sun || __NetBSD__ || __KFBSD__ || __OpenBSD__ || __DragonFly__ return bsd_reg_read(dbg, type, buf, size); #else #warning dbg-native not supported for this platform return false; #endif } static int rz_debug_native_reg_write(RzDebug *dbg, int type, const ut8 *buf, int size) { // XXX use switch or so if (type == RZ_REG_TYPE_DRX) { #if __i386__ || __x86_64__ #if __APPLE__ return xnu_reg_write(dbg, type, buf, size); #elif __WINDOWS__ return w32_reg_write(dbg, type, buf, size); #elif __linux__ return linux_reg_write(dbg, type, buf, size); #else return bsd_reg_write(dbg, type, buf, size); #endif #else // i386/x86-64 return false; #endif } else if (type == RZ_REG_TYPE_GPR) { #if __APPLE__ return xnu_reg_write(dbg, type, buf, size); #elif __WINDOWS__ return w32_reg_write(dbg, type, buf, size); #elif __linux__ return linux_reg_write(dbg, type, buf, size); #elif __sun int ret = ptrace(PTRACE_SETREGS, dbg->pid, (void *)(size_t)buf, sizeof(RZ_DEBUG_REG_T)); if (sizeof(RZ_DEBUG_REG_T) < size) size = sizeof(RZ_DEBUG_REG_T); return ret == 0; #else return bsd_reg_write(dbg, type, buf, size); #endif } else if (type == RZ_REG_TYPE_FPU) { #if __linux__ return linux_reg_write(dbg, type, buf, size); #elif __APPLE__ return false; #elif __WINDOWS__ return false; #else return bsd_reg_write(dbg, type, buf, size); #endif } // else eprintf ("TODO: reg_write_non-gpr (%d)\n", type); return false; } #if __linux__ static int io_perms_to_prot(int io_perms) { int prot_perms = PROT_NONE; if (io_perms & RZ_PERM_R) { prot_perms |= PROT_READ; } if (io_perms & RZ_PERM_W) { prot_perms |= PROT_WRITE; } if (io_perms & RZ_PERM_X) { prot_perms |= PROT_EXEC; } return prot_perms; } #if __linux__ static int sys_thp_mode(void) { size_t i; const char *thp[] = { "/sys/kernel/mm/transparent_hugepage/enabled", "/sys/kernel/mm/redhat_transparent_hugepage/enabled", }; int ret = 0; for (i = 0; i < RZ_ARRAY_SIZE(thp); i++) { char *val = rz_file_slurp(thp[i], NULL); if (val) { if (strstr(val, "[madvise]")) { ret = 1; } else if (strstr(val, "[always]")) { ret = 2; } free(val); break; } } return ret; } #endif static int linux_map_thp(RzDebug *dbg, ut64 addr, int size) { #if !defined(__ANDROID__) && defined(MADV_HUGEPAGE) RzBuffer *buf = NULL; char code[1024]; int ret = true; char *asm_list[] = { "x86", "x86.as", "x64", "x86.as", NULL }; // In architectures where rizin is supported, arm and x86, it is 2MB const size_t thpsize = 1 << 21; if ((size % thpsize)) { eprintf("size not a power of huge pages size\n"); return false; } #if __linux__ // In always mode, is more into mmap syscall level // even though the address might not have the 'hg' // vmflags if (sys_thp_mode() != 1) { eprintf("transparent huge page mode is not in madvise mode\n"); return false; } #endif int num = rz_syscall_get_num(dbg->analysis->syscall, "madvise"); snprintf(code, sizeof(code), "sc_madvise@syscall(%d);\n" "main@naked(0) { .rarg0 = sc_madvise(0x%08" PFMT64x ",%d, %d);break;\n" "}\n", num, addr, size, MADV_HUGEPAGE); rz_egg_reset(dbg->egg); rz_egg_setup(dbg->egg, dbg->arch, 8 * dbg->bits, 0, 0); rz_egg_load(dbg->egg, code, 0); if (!rz_egg_compile(dbg->egg)) { eprintf("Cannot compile.\n"); goto err_linux_map_thp; } if (!rz_egg_assemble_asm(dbg->egg, asm_list)) { eprintf("rz_egg_assemble: invalid assembly\n"); goto err_linux_map_thp; } buf = rz_egg_get_bin(dbg->egg); if (buf) { rz_reg_arena_push(dbg->reg); ut64 tmpsz; const ut8 *tmp = rz_buf_data(buf, &tmpsz); ret = rz_debug_execute(dbg, tmp, tmpsz, 1) == 0; rz_reg_arena_pop(dbg->reg); } err_linux_map_thp: return ret; #else return false; #endif } static RzDebugMap *linux_map_alloc(RzDebug *dbg, ut64 addr, int size, bool thp) { RzBuffer *buf = NULL; RzDebugMap *map = NULL; char code[1024], *sc_name; int num; /* force to usage of x86.as, not yet working x86.nz */ char *asm_list[] = { "x86", "x86.as", "x64", "x86.as", NULL }; /* NOTE: Since kernel 2.4, that system call has been superseded by mmap2(2 and nowadays the glibc mmap() wrapper function invokes mmap2(2)). If arch is x86_32 then usage mmap2() */ if (!strcmp(dbg->arch, "x86") && dbg->bits == 4) { sc_name = "mmap2"; } else { sc_name = "mmap"; } num = rz_syscall_get_num(dbg->analysis->syscall, sc_name); #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS 0x20 #endif snprintf(code, sizeof(code), "sc_mmap@syscall(%d);\n" "main@naked(0) { .rarg0 = sc_mmap(0x%08" PFMT64x ",%d,%d,%d,%d,%d);break;\n" "}\n", num, addr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); rz_egg_reset(dbg->egg); rz_egg_setup(dbg->egg, dbg->arch, 8 * dbg->bits, 0, 0); rz_egg_load(dbg->egg, code, 0); if (!rz_egg_compile(dbg->egg)) { eprintf("Cannot compile.\n"); goto err_linux_map_alloc; } if (!rz_egg_assemble_asm(dbg->egg, asm_list)) { eprintf("rz_egg_assemble: invalid assembly\n"); goto err_linux_map_alloc; } buf = rz_egg_get_bin(dbg->egg); if (buf) { ut64 map_addr; rz_reg_arena_push(dbg->reg); ut64 tmpsz; const ut8 *tmp = rz_buf_data(buf, &tmpsz); map_addr = rz_debug_execute(dbg, tmp, tmpsz, 1); rz_reg_arena_pop(dbg->reg); if (map_addr != (ut64)-1) { if (thp) { if (!linux_map_thp(dbg, map_addr, size)) { // Not overly dramatic eprintf("map promotion to huge page failed\n"); } } rz_debug_map_sync(dbg); map = rz_debug_map_get(dbg, map_addr); } } err_linux_map_alloc: return map; } static int linux_map_dealloc(RzDebug *dbg, ut64 addr, int size) { RzBuffer *buf = NULL; char code[1024]; int ret = 0; char *asm_list[] = { "x86", "x86.as", "x64", "x86.as", NULL }; int num = rz_syscall_get_num(dbg->analysis->syscall, "munmap"); snprintf(code, sizeof(code), "sc_munmap@syscall(%d);\n" "main@naked(0) { .rarg0 = sc_munmap(0x%08" PFMT64x ",%d);break;\n" "}\n", num, addr, size); rz_egg_reset(dbg->egg); rz_egg_setup(dbg->egg, dbg->arch, 8 * dbg->bits, 0, 0); rz_egg_load(dbg->egg, code, 0); if (!rz_egg_compile(dbg->egg)) { eprintf("Cannot compile.\n"); goto err_linux_map_dealloc; } if (!rz_egg_assemble_asm(dbg->egg, asm_list)) { eprintf("rz_egg_assemble: invalid assembly\n"); goto err_linux_map_dealloc; } buf = rz_egg_get_bin(dbg->egg); if (buf) { rz_reg_arena_push(dbg->reg); ut64 tmpsz; const ut8 *tmp = rz_buf_data(buf, &tmpsz); ret = rz_debug_execute(dbg, tmp, tmpsz, 1) == 0; rz_reg_arena_pop(dbg->reg); } err_linux_map_dealloc: return ret; } #endif static RzDebugMap *rz_debug_native_map_alloc(RzDebug *dbg, ut64 addr, int size, bool thp) { #if __APPLE__ (void)thp; return xnu_map_alloc(dbg, addr, size); #elif __WINDOWS__ (void)thp; return w32_map_alloc(dbg, addr, size); #elif __linux__ return linux_map_alloc(dbg, addr, size, thp); #else // malloc not implemented for this platform return NULL; #endif } static int rz_debug_native_map_dealloc(RzDebug *dbg, ut64 addr, int size) { #if __APPLE__ return xnu_map_dealloc(dbg, addr, size); #elif __WINDOWS__ return w32_map_dealloc(dbg, addr, size); #elif __linux__ return linux_map_dealloc(dbg, addr, size); #else // mdealloc not implemented for this platform return false; #endif } #if !__WINDOWS__ && !__APPLE__ static void _map_free(RzDebugMap *map) { if (!map) { return; } free(map->name); free(map->file); free(map); } #endif static RzList /**/ *rz_debug_native_map_get(RzDebug *dbg) { RzList *list = NULL; #if __KFBSD__ int ign; char unkstr[PROC_UNKSTR_SZ + 1]; #endif #if __APPLE__ list = xnu_dbg_maps(dbg, 0); #elif __WINDOWS__ list = rz_w32_dbg_maps(dbg); #else #if __sun char path[1024]; /* TODO: On solaris parse /proc/%d/map */ snprintf(path, sizeof(path) - 1, "pmap %d >&2", ps.tid); rz_sys_xsystem(path); #else RzDebugMap *map; int i, perm, unk = 0; char *pos_c; char path[1024], line[1024], name[PROC_NAME_SZ + 1]; char region[PROC_REGION_SZ + 1], region2[PROC_REGION_SZ + 1], perms[PROC_PERM_SZ + 1]; FILE *fd; if (dbg->pid == -1) { // eprintf ("rz_debug_native_map_get: No selected pid (-1)\n"); return NULL; } /* prepend 0x prefix */ region[0] = region2[0] = '0'; region[1] = region2[1] = 'x'; #if __OpenBSD__ /* OpenBSD has no procfs, so no idea trying. */ return bsd_native_sysctl_map(dbg); #endif #if __KFBSD__ list = bsd_native_sysctl_map(dbg); if (list) { return list; } snprintf(path, sizeof(path), "/proc/%d/map", dbg->pid); #else snprintf(path, sizeof(path), "/proc/%d/maps", dbg->pid); #endif fd = rz_sys_fopen(path, "r"); if (!fd) { perror(sdb_fmt("Cannot open '%s'", path)); return NULL; } list = rz_list_new(); if (!list) { fclose(fd); return NULL; } list->free = (RzListFree)_map_free; while (!feof(fd)) { size_t line_len; bool map_is_shared = false; ut64 map_start, map_end; if (!fgets(line, sizeof(line), fd)) { break; } /* kill the newline if we got one */ line_len = strlen(line); if (line[line_len - 1] == '\n') { line[line_len - 1] = '\0'; line_len--; } /* maps files should not have empty lines */ if (line_len == 0) { break; } #if __KFBSD__ // 0x8070000 0x8072000 2 0 0xc1fde948 rw- 1 0 0x2180 COW NC vnode /usr/bin/gcc if (sscanf(line, "%" RZ_STR_DEF(PROC_REGION_LEFT_SZ) "s %" RZ_STR_DEF(PROC_REGION_LEFT_SZ) "s %d %d 0x%" RZ_STR_DEF(PROC_UNKSTR_SZ) "s %3s %d %d", ®ion[2], ®ion2[2], &ign, &ign, unkstr, perms, &ign, &ign) != 8) { eprintf("%s: Unable to parse \"%s\"\n", __func__, path); rz_list_free(list); return NULL; } /* snag the file name */ pos_c = strchr(line, '/'); if (pos_c) { strncpy(name, pos_c, sizeof(name) - 1); } else { name[0] = '\0'; } #else ut64 offset = 0; ; // 7fc8124c4000-7fc81278d000 r--p 00000000 fc:00 17043921 /usr/lib/locale/locale-archive i = sscanf(line, "%" RZ_STR_DEF(PROC_REGION_LEFT_SZ) "s %" RZ_STR_DEF(PROC_PERM_SZ) "s %08" PFMT64x " %*s %*s %" RZ_STR_DEF(PROC_NAME_SZ) "[^\n]", ®ion[2], perms, &offset, name); if (i == 3) { name[0] = '\0'; } else if (i != 4) { eprintf("%s: Unable to parse \"%s\"\n", __func__, path); eprintf("%s: problematic line: %s\n", __func__, line); rz_list_free(list); return NULL; } /* split the region in two */ pos_c = strchr(®ion[2], '-'); if (!pos_c) { // should this be an error? continue; } strncpy(®ion2[2], pos_c + 1, sizeof(region2) - 2 - 1); #endif // __KFBSD__ if (!*name) { snprintf(name, sizeof(name), "unk%d", unk++); } perm = 0; for (i = 0; i < 5 && perms[i]; i++) { switch (perms[i]) { case 'r': perm |= RZ_PERM_R; break; case 'w': perm |= RZ_PERM_W; break; case 'x': perm |= RZ_PERM_X; break; case 'p': map_is_shared = false; break; case 's': map_is_shared = true; break; } } map_start = rz_num_get(NULL, region); map_end = rz_num_get(NULL, region2); if (map_start == map_end || map_end == 0) { eprintf("%s: ignoring invalid map size: %s - %s\n", __func__, region, region2); continue; } map = rz_debug_map_new(name, map_start, map_end, perm, 0); if (!map) { break; } #if __linux__ map->offset = offset; map->shared = map_is_shared; #endif map->file = strdup(name); rz_list_append(list, map); } fclose(fd); #endif // __sun #endif // __APPLE__ return list; } static RzList /**/ *rz_debug_native_modules_get(RzDebug *dbg) { char *lastname = NULL; RzDebugMap *map; RzListIter *iter, *iter2; RzList *list, *last; bool must_delete; #if __APPLE__ list = xnu_dbg_maps(dbg, 1); if (list && !rz_list_empty(list)) { return list; } #elif __WINDOWS__ list = rz_w32_dbg_modules(dbg); if (list && !rz_list_empty(list)) { return list; } #endif if (!(list = rz_debug_native_map_get(dbg))) { return NULL; } if (!(last = rz_list_newf((RzListFree)rz_debug_map_free))) { rz_list_free(list); return NULL; } rz_list_foreach_safe (list, iter, iter2, map) { const char *file = map->file; if (!map->file) { file = map->file = strdup(map->name); } must_delete = true; if (file && *file == '/') { if (!lastname || strcmp(lastname, file)) { must_delete = false; } } if (must_delete) { rz_list_delete(list, iter); } else { rz_list_append(last, map); free(lastname); lastname = strdup(file); } } list->free = NULL; free(lastname); rz_list_free(list); return last; } static bool rz_debug_native_kill(RzDebug *dbg, int pid, int tid, int sig) { bool ret = false; if (pid == 0) { pid = dbg->pid; } #if __WINDOWS__ ret = w32_kill(dbg, pid, tid, sig); #else #if 0 if (thread) { // XXX this is linux>2.5 specific..ugly if (dbg->tid>0 && (ret = tgkill (dbg->pid, dbg->tid, sig))) { if (ret != -1) ret = true; } } else { #endif if (sig == SIGKILL && dbg->threads) { rz_list_free(dbg->threads); dbg->threads = NULL; } if ((rz_sys_kill(pid, sig) != -1)) { ret = true; } if (errno == 1) { ret = -true; // EPERM } #if 0 // } #endif #endif return ret; } struct rz_debug_desc_plugin_t rz_debug_desc_plugin_native; static bool rz_debug_native_init(RzDebug *dbg, void **user) { dbg->cur->desc = rz_debug_desc_plugin_native; #if __WINDOWS__ return w32_init(dbg); #elif __APPLE__ return rz_xnu_debug_init(dbg, user); #else return true; #endif } static void rz_debug_native_fini(RzDebug *dbg, void *user) { #if __APPLE__ rz_xnu_debug_fini(dbg, user); #endif } #if __i386__ || __x86_64__ static void sync_drx_regs(RzDebug *dbg, drxt *regs, size_t num_regs) { /* sanity check, we rely on this assumption */ if (num_regs != NUM_DRX_REGISTERS) { eprintf("drx: Unsupported number of registers for get_debug_regs\n"); return; } // sync drx regs #define R dbg->reg regs[0] = rz_reg_getv(R, "dr0"); regs[1] = rz_reg_getv(R, "dr1"); regs[2] = rz_reg_getv(R, "dr2"); regs[3] = rz_reg_getv(R, "dr3"); /* RESERVED regs[4] = rz_reg_getv (R, "dr4"); regs[5] = rz_reg_getv (R, "dr5"); */ regs[6] = rz_reg_getv(R, "dr6"); regs[7] = rz_reg_getv(R, "dr7"); } #endif #if __i386__ || __x86_64__ static void set_drx_regs(RzDebug *dbg, drxt *regs, size_t num_regs) { /* sanity check, we rely on this assumption */ if (num_regs != NUM_DRX_REGISTERS) { eprintf("drx: Unsupported number of registers for get_debug_regs\n"); return; } #define R dbg->reg rz_reg_setv(R, "dr0", regs[0]); rz_reg_setv(R, "dr1", regs[1]); rz_reg_setv(R, "dr2", regs[2]); rz_reg_setv(R, "dr3", regs[3]); rz_reg_setv(R, "dr6", regs[6]); rz_reg_setv(R, "dr7", regs[7]); } #endif static int rz_debug_native_drx(RzDebug *dbg, int n, ut64 addr, int sz, int rwx, int g, int api_type) { #if __i386__ || __x86_64__ int retval = false; drxt regs[NUM_DRX_REGISTERS] = { 0 }; // sync drx regs sync_drx_regs(dbg, regs, NUM_DRX_REGISTERS); switch (api_type) { case DRX_API_LIST: drx_list(regs); retval = false; break; case DRX_API_GET_BP: /* get the index of the breakpoint at addr */ retval = drx_get_at(regs, addr); break; case DRX_API_REMOVE_BP: /* remove hardware breakpoint */ drx_set(regs, n, addr, -1, 0, 0); retval = true; break; case DRX_API_SET_BP: /* set hardware breakpoint */ drx_set(regs, n, addr, sz, rwx, g); retval = true; break; default: /* this should not happen, someone misused the API */ eprintf("drx: Unsupported api type in rz_debug_native_drx\n"); retval = false; } set_drx_regs(dbg, regs, NUM_DRX_REGISTERS); return retval; #else eprintf("drx: Unsupported platform\n"); #endif return -1; } #if __linux__ #if __arm__ || __arm64__ || __aarch64__ #include #include #define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ #define NT_ARM_TLS 0x401 /* ARM TLS register */ #define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ #define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ #ifndef PTRACE_GETHBPREGS #define PTRACE_GETHBPREGS 29 #define PTRACE_SETHBPREGS 30 #endif #if __arm__ static bool ll_arm32_hwbp_set(pid_t pid, ut64 addr, int size, int wp, int type) { const unsigned byte_mask = (1 << size) - 1; // const unsigned type = 2; // Write. const unsigned enable = 1; const unsigned control = byte_mask << 5 | type << 3 | enable; (void)ptrace(PTRACE_SETHBPREGS, pid, -1, (void *)(size_t)addr); return ptrace(PTRACE_SETHBPREGS, pid, -2, &control) != -1; } static bool arm32_hwbp_add(RzDebug *dbg, RzBreakpoint *bp, RzBreakpointItem *b) { return ll_arm32_hwbp_set(dbg->pid, b->addr, b->size, 0, 1 | 2 | 4); } static bool arm32_hwbp_del(RzDebug *dbg, RzBreakpoint *bp, RzBreakpointItem *b) { return false; // TODO: hwbp.del not yetimplemented } #endif // PTRACE_GETHWBPREGS #endif // __arm #if (__arm64__ || __aarch64__) && defined(PTRACE_GETREGSET) // type = 2 = write // static volatile uint8_t var[96] __attribute__((__aligned__(32))); static bool ll_arm64_hwbp_set(pid_t pid, ut64 _addr, int size, int wp, ut32 type) { const volatile uint8_t *addr = (void *)(size_t)_addr; //&var[32 + wp]; const unsigned int offset = (uintptr_t)addr % 8; const ut32 byte_mask = ((1 << size) - 1) << offset; const ut32 enable = 1; const ut32 control = byte_mask << 5 | type << 3 | enable; struct user_hwdebug_state dreg_state = { 0 }; struct iovec iov = { 0 }; iov.iov_base = &dreg_state; iov.iov_len = sizeof(dreg_state); if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_HW_WATCH, &iov) == -1) { // error reading regs } memcpy(&dreg_state, iov.iov_base, sizeof(dreg_state)); // wp is not honored here i think... we can't have more than one wp for now.. dreg_state.dbg_regs[0].addr = (uintptr_t)(addr - offset); dreg_state.dbg_regs[0].ctrl = control; iov.iov_base = &dreg_state; iov.iov_len = rz_offsetof(struct user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]); if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0) { return true; } if (errno == EIO) { eprintf("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n", strerror(errno)); } eprintf("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n", strerror(errno)); return false; } static bool ll_arm64_hwbp_del(pid_t pid, ut64 _addr, int size, int wp, ut32 type) { // const volatile uint8_t *addr = &var[32 + wp]; // TODO: support multiple watchpoints and find struct user_hwdebug_state dreg_state = { 0 }; struct iovec iov = { 0 }; iov.iov_base = &dreg_state; // only delete 1 bp for now iov.iov_len = rz_offsetof(struct user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]); if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0) { return true; } if (errno == EIO) { eprintf("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n", strerror(errno)); } eprintf("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n", strerror(errno)); return false; } static bool arm64_hwbp_add(RzDebug *dbg, RzBreakpoint *bp, RzBreakpointItem *b) { return ll_arm64_hwbp_set(dbg->pid, b->addr, b->size, 0, 1 | 2 | 4); } static bool arm64_hwbp_del(RzDebug *dbg, RzBreakpoint *bp, RzBreakpointItem *b) { return ll_arm64_hwbp_del(dbg->pid, b->addr, b->size, 0, 1 | 2 | 4); } #endif // __arm64__ #endif // __linux__ /* * set or unset breakpoints... * * we only handle the case for hardware breakpoints here. otherwise, * we let the caller handle the work. */ static int rz_debug_native_bp(RzBreakpoint *bp, RzBreakpointItem *b, bool set) { if (b && b->hw) { #if __i386__ || __x86_64__ return set ? drx_add((RzDebug *)bp->user, bp, b) : drx_del((RzDebug *)bp->user, bp, b); #elif (__arm64__ || __aarch64__) && __linux__ return set ? arm64_hwbp_add((RzDebug *)bp->user, bp, b) : arm64_hwbp_del((RzDebug *)bp->user, bp, b); #elif __WINDOWS__ return set ? w32_hwbp_arm_add((RzDebug *)bp->user, bp, b) : w32_hwbp_arm_del((RzDebug *)bp->user, bp, b); #elif __arm__ && __linux__ return set ? arm32_hwbp_add((RzDebug *)bp->user, bp, b) : arm32_hwbp_del((RzDebug *)bp->user, bp, b); #endif } return false; } #if __APPLE__ static int getMaxFiles(void) { struct rlimit limit; if (getrlimit(RLIMIT_NOFILE, &limit) != 0) { return 1024; } return limit.rlim_cur; } static RzList *xnu_desc_list(int pid) { #if TARGET_OS_IPHONE || __POWERPC__ return NULL; #else #define xwrz_testwx(x) ((x & 1) << 2) | (x & 2) | ((x & 4) >> 2) RzDebugDesc *desc; RzList *ret = rz_list_new(); struct vnode_fdinfowithpath vi; int i, nb, type = 0; int maxfd = getMaxFiles(); for (i = 0; i < maxfd; i++) { nb = proc_pidfdinfo(pid, i, PROC_PIDFDVNODEPATHINFO, &vi, sizeof(vi)); if (nb < 1) { continue; } if (nb < sizeof(vi)) { perror("too few bytes"); break; } // printf ("FD %d RWX %x ", i, vi.pfi.fi_openflags); // printf ("PATH %s\n", vi.pvip.vip_path); desc = rz_debug_desc_new(i, vi.pvip.vip_path, xwrz_testwx(vi.pfi.fi_openflags), type, 0); rz_list_append(ret, desc); } return ret; #endif } #endif static RzList /**/ *rz_debug_desc_native_list(int pid) { #if __APPLE__ return xnu_desc_list(pid); #elif __WINDOWS__ return w32_desc_list(pid); #elif __KFBSD__ || __NetBSD__ return bsd_desc_list(pid); #elif __linux__ return linux_desc_list(pid); #else #warning list filedescriptors not supported for this platform return NULL; #endif } static int rz_debug_native_map_protect(RzDebug *dbg, ut64 addr, int size, int perms) { #if __WINDOWS__ return w32_map_protect(dbg, addr, size, perms); #elif __APPLE__ return xnu_map_protect(dbg, addr, size, perms); #elif __linux__ RzBuffer *buf = NULL; char code[1024]; int num; num = rz_syscall_get_num(dbg->analysis->syscall, "mprotect"); snprintf(code, sizeof(code), "sc@syscall(%d);\n" "main@global(0) { sc(%p,%d,%d);\n" ":int3\n" "}\n", num, (void *)(size_t)addr, size, io_perms_to_prot(perms)); rz_egg_reset(dbg->egg); rz_egg_setup(dbg->egg, dbg->arch, 8 * dbg->bits, 0, 0); rz_egg_load(dbg->egg, code, 0); if (!rz_egg_compile(dbg->egg)) { eprintf("Cannot compile.\n"); return false; } if (!rz_egg_assemble(dbg->egg)) { eprintf("rz_egg_assemble: invalid assembly\n"); return false; } buf = rz_egg_get_bin(dbg->egg); if (buf) { rz_reg_arena_push(dbg->reg); ut64 tmpsz; const ut8 *tmp = rz_buf_data(buf, &tmpsz); rz_debug_execute(dbg, tmp, tmpsz, 1); rz_reg_arena_pop(dbg->reg); return true; } return false; #else // mprotect not implemented for this platform return false; #endif } static int rz_debug_desc_native_open(const char *path) { return 0; } #if 0 static int rz_debug_setup_ownership (int fd, RzDebug *dbg) { RzDebugInfo *info = rz_debug_info (dbg, NULL); if (!info) { eprintf ("Error while getting debug info.\n"); return -1; } fchown (fd, info->uid, info->gid); rz_debug_info_free (info); return 0; } #endif static bool rz_debug_gcore(RzDebug *dbg, char *path, RzBuffer *dest) { #if __APPLE__ (void)path; return xnu_generate_corefile(dbg, dest); #elif __linux__ && (__x86_64__ || __i386__ || __arm__ || __arm64__) (void)path; #if __ANDROID__ return false; #else return linux_generate_corefile(dbg, dest); #endif #elif __KFBSD__ || __NetBSD__ return bsd_generate_corefile(dbg, path, dest); #else return false; #endif } struct rz_debug_desc_plugin_t rz_debug_desc_plugin_native = { .open = rz_debug_desc_native_open, .list = rz_debug_desc_native_list, }; RzDebugPlugin rz_debug_plugin_native = { .name = "native", .license = "LGPL3", #if __i386__ .bits = RZ_SYS_BITS_32, .arch = "x86", .canstep = 1, #elif __x86_64__ .bits = RZ_SYS_BITS_32 | RZ_SYS_BITS_64, .arch = "x86", .canstep = 1, // XXX it's 1 on some platforms... #elif __aarch64__ || __arm64__ .bits = RZ_SYS_BITS_16 | RZ_SYS_BITS_32 | RZ_SYS_BITS_64, .arch = "arm", #if __WINDOWS__ .canstep = 0, #else .canstep = 1, #endif #elif __arm__ .bits = RZ_SYS_BITS_16 | RZ_SYS_BITS_32 | RZ_SYS_BITS_64, .arch = "arm", .canstep = 0, #elif __mips__ .bits = RZ_SYS_BITS_32 | RZ_SYS_BITS_64, .arch = "mips", .canstep = 0, #elif __powerpc__ #if __powerpc64__ .bits = RZ_SYS_BITS_32 | RZ_SYS_BITS_64, #else .bits = RZ_SYS_BITS_32, #endif .arch = "ppc", .canstep = 1, #else .bits = 0, .arch = 0, .canstep = 0, .arch = "unsupported", #ifdef _MSC_VER #pragma message("Unsupported architecture") #else #warning Unsupported architecture #endif #endif .init = &rz_debug_native_init, .fini = &rz_debug_native_fini, .step = &rz_debug_native_step, .cont = &rz_debug_native_continue, .stop = &rz_debug_native_stop, .contsc = &rz_debug_native_continue_syscall, .attach = &rz_debug_native_attach, .detach = &rz_debug_native_detach, // TODO: add native select for other platforms? #if __WINDOWS__ || __linux__ .select = &rz_debug_native_select, #endif .pids = &rz_debug_native_pids, .threads = &rz_debug_native_threads, .wait = &rz_debug_native_wait, .kill = &rz_debug_native_kill, .frames = &rz_debug_native_frames, // rename to backtrace ? .reg_profile = rz_debug_native_reg_profile, .reg_read = rz_debug_native_reg_read, .info = rz_debug_native_info, .reg_write = (void *)&rz_debug_native_reg_write, .map_alloc = rz_debug_native_map_alloc, .map_dealloc = rz_debug_native_map_dealloc, .map_get = rz_debug_native_map_get, .modules_get = rz_debug_native_modules_get, .map_protect = rz_debug_native_map_protect, .breakpoint = rz_debug_native_bp, .drx = rz_debug_native_drx, .gcore = rz_debug_gcore, }; #ifndef RZ_PLUGIN_INCORE RZ_API RzLibStruct rizin_plugin = { .type = RZ_LIB_TYPE_DBG, .data = &rz_debug_plugin_native, .version = RZ_VERSION }; #endif // RZ_PLUGIN_INCORE // #endif #else // DEBUGGER RzDebugPlugin rz_debug_plugin_native = { NULL // .name = "native", }; #endif // DEBUGGER