rizin/libr/debug/p/debug_native.c
pancake 4db5cded34 * Fix w32dist find command
* Display TODO message in r2 -L
* Implement rax2 -s - and rax2 -
* Honor dbg->tid in many places
* Implement basic stuff for dbg->reason
  - Only w32 and *nix atm
  - Not yet displayed or used
  - Added many new reason types in enum
* Fix w32 register map
* Handle attach:// in io_w32dbg plugin
* Implement w32 process and thread list
  - You can now select the thread
  > dp      # list pids
  > dp=1424 # attach to pid
  > dpt     # list threads
  > dpt=580 # select thread
* Added dummy code to list windows
2010-11-17 00:56:48 +01:00

1451 lines
37 KiB
C

/* radare - LGPL - Copyright 2009-2010 pancake<nopcode.org> */
#include <r_userconf.h>
#include <r_debug.h>
#include <r_asm.h>
#include <r_reg.h>
#include <r_lib.h>
#include <signal.h>
static int r_debug_native_continue(int pid, int sig);
static int r_debug_native_reg_read(RDebug *dbg, int type, ut8 *buf, int size);
static int r_debug_native_reg_write(int pid, int tid, int type, const ut8* buf, int size);
#define DEBUGGER 1
#define MAXBT 128
#if __WINDOWS__
#include <windows.h>
#define R_DEBUG_REG_T CONTEXT
#include "native/w32.c"
#elif __OpenBSD__ || __NetBSD__ || __FreeBSD__
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#define R_DEBUG_REG_T struct reg
#elif __APPLE__
#define MACH_ERROR_STRING(ret) \
(mach_error_string (ret) ? r_str_get (mach_error_string (ret)) : "(unknown)")
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <mach/exception_types.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/mach_interface.h>
#include <mach/mach_traps.h>
#include <mach/mach_types.h>
#include <mach/mach_vm.h>
#include <mach/mach_error.h>
#include <mach/task.h>
#include <mach/task_info.h>
#include <mach/thread_act.h>
#include <mach/thread_info.h>
#include <mach/vm_map.h>
#include <mach-o/loader.h>
#include <mach-o/nlist.h>
#include <errno.h>
#include <unistd.h>
#include <sys/sysctl.h>
#include <sys/fcntl.h>
#include <sys/proc.h>
#if __POWERPC__
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <mach/ppc/_types.h>
#include <mach/ppc/thread_status.h>
#define R_DEBUG_REG_T ppc_thread_state_t
#define R_DEBUG_STATE_T PPC_THREAD_STATE
#elif __arm
#include <mach/arm/thread_status.h>
#define R_DEBUG_REG_T arm_thread_state_t
#define R_DEBUG_STATE_T ARM_THREAD_STATE
#else
#include <mach/i386/thread_status.h>
#include <sys/ucontext.h>
#define R_DEBUG_REG_T _STRUCT_X86_THREAD_STATE32
#include <mach/i386/_structs.h>
#define R_DEBUG_STATE_T i386_THREAD_STATE
#endif
#elif __sun
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#define R_DEBUG_REG_T gregset_t
#undef DEBUGGER
#define DEBUGGER 0
#warning No debugger support for SunOS yet
#elif __linux__
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <limits.h>
# if __i386__ || __x86_64__
# define R_DEBUG_REG_T struct user_regs_struct
# elif __arm__
# define R_DEBUG_REG_T struct user_regs
# elif __mips__
#include <sys/ucontext.h>
typedef unsigned long mips64_regs_t [4096];
# define R_DEBUG_REG_T mips64_regs_t
# endif
#else // OS
#warning Unsupported debugging platform
#undef DEBUGGER
#define DEBUGGER 0
#endif // ARCH
#if DEBUGGER
#if __APPLE__
// TODO: move into native/
task_t pid_to_task(int pid) {
static task_t old_pid = -1;
static task_t old_task = -1;
task_t task = 0;
int err;
/* xlr8! */
if (old_task!= -1) //old_pid != -1 && old_pid == pid)
return old_task;
err = task_for_pid (mach_task_self(), (pid_t)pid, &task);
if ((err != KERN_SUCCESS) || !MACH_PORT_VALID(task)) {
eprintf ("Failed to get task %d for pid %d.\n", (int)task, (int)pid);
eprintf ("Reason: 0x%x: %s\n", err, (char *)MACH_ERROR_STRING (err));
eprintf ("You probably need to add user to procmod group.\n"
" Or chmod g+s radare && chown root:procmod radare\n");
eprintf ("FMI: http://developer.apple.com/documentation/Darwin/Reference/ManPages/man8/taskgated.8.html\n");
return -1;
}
old_pid = pid;
old_task = task;
return task;
}
// XXX intel specific -- generalize in r_reg..ease access
#define EFLAGS_TRAP_FLAG 0x100
static inline void debug_arch_x86_trap_set(RDebug *dbg, int foo) {
#if __i386__ || __x86_64__
R_DEBUG_REG_T regs;
r_debug_native_reg_read (dbg, R_REG_TYPE_GPR, &regs, sizeof (regs));
eprintf ("trap flag: %d\n", (regs.__eflags&0x100));
if (foo) regs.__eflags |= EFLAGS_TRAP_FLAG;
else regs.__eflags &= ~EFLAGS_TRAP_FLAG;
r_debug_native_reg_write (dbg->pid, dbg->tid, R_REG_TYPE_GPR, &regs, sizeof (regs));
#endif
}
#endif // __APPLE__
static int r_debug_native_step(RDebug *dbg, int pid) {
int ret = R_FALSE;
#if __WINDOWS__
CONTEXT regs;
//R_DEBUG_REG_T regs;
regs.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
// GetTheadContext (tid2hnd
// XXX TODO CONTINUE h
/* up TRAP flag */
r_debug_native_reg_read (dbg, R_REG_TYPE_GPR, &regs, sizeof (regs));
regs.EFlags |= 0x100;
r_debug_native_reg_write (dbg->pid, dbg->tid, R_REG_TYPE_GPR, &regs, sizeof (regs));
//single_step = pid;
r_debug_native_continue (pid, -1);
#elif __APPLE__
debug_arch_x86_trap_set (dbg, 1);
// TODO: not supported in all platforms. need dbg.swstep=
#if __arm__
if (!dbg->swstep)
eprintf ("XXX hardware stepping is not supported in arm. set e dbg.swstep=true\n");
else eprintf ("XXX: software step is not implemented??\n");
return R_FALSE;
#endif
//eprintf ("stepping from pc = %08x\n", (ut32)get_offset("eip"));
//ret = ptrace (PT_STEP, ps.tid, (caddr_t)get_offset("eip"), SIGSTOP);
ret = ptrace (PT_STEP, pid, (caddr_t)1, SIGTRAP); //SIGINT);
if (ret != 0) {
perror ("ptrace-step");
eprintf ("mach-error: %d, %s\n", ret, MACH_ERROR_STRING (ret));
ret = R_FALSE; /* do not wait for events */
} else ret = R_TRUE;
#else // linux
ut32 addr = 0; /* should be eip */
//ut32 data = 0;
//printf("NATIVE STEP over PID=%d\n", pid);
ret = ptrace (PTRACE_SINGLESTEP, pid, addr, 0); //addr, data);
if (ret == -1) {
perror ("native-singlestep");
ret = R_FALSE;
} else ret = R_TRUE;
#endif
return ret;
}
static int r_debug_native_attach(int pid) {
int ret = -1;
#if __WINDOWS__
HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess != (HANDLE)NULL && DebugActiveProcess (pid)) {
w32_dbg_threads (pid);
ret = 0;
} else ret = -1;
#elif __APPLE__
ret = ptrace (PT_ATTACH, pid, 0, 0);
#else
ret = ptrace (PTRACE_ATTACH, pid, 0, 0);
eprintf ("attach=%d\n", ret);
#endif
return (ret != -1)?R_TRUE:R_FALSE;
}
static int r_debug_native_detach(int pid) {
#if __WINDOWS__
return w32_detach (pid)? 0 : -1;
#elif __APPLE__
return ptrace (PT_DETACH, pid, NULL, 0);
#else
return ptrace (PTRACE_DETACH, pid, NULL, NULL);
#endif
}
static int r_debug_native_continue_syscall(int pid, int num) {
#if __linux__
return ptrace (PTRACE_SYSCALL, pid, 0, 0);
#elif __BSD__
ut64 pc = 0LL; // XXX
return ptrace (PTRACE_SYSCALL, pid, pc, 0);
#else
eprintf ("TODO: continue syscall not implemented yet\n");
return -1;
#endif
}
/* TODO: specify thread? */
/* TODO: must return true/false */
static int r_debug_native_continue(int pid, int sig) {
void *data = NULL;
if (sig != -1)
data = (void*)(size_t)sig;
#if __WINDOWS__
if (ContinueDebugEvent (pid, pid, DBG_CONTINUE) == 0) {
eprintf ("debug_contp: error\n");
return -1;
}
return 0;
#elif __APPLE__
eprintf ("debug_contp: program is now running...\n");
/* XXX */
/* only stopped with ptrace the first time */
//ptrace(PT_CONTINUE, pid, 0, 0);
ptrace (PT_DETACH, pid, 0, 0);
#if 0
task_resume (inferior_task); // ???
thread_resume (inferior_threads[0]);
#endif
return 0;
#else
void *addr = NULL; // eip for BSD
return ptrace (PTRACE_CONT, pid, addr, data);
#endif
}
static int r_debug_native_wait(int pid) {
#if __WINDOWS__
return w32_dbg_wait (pid);
#else
int ret, status = -1;
//printf ("prewait\n");
ret = waitpid (pid, &status, 0);
//printf ("status=%d (return=%d)\n", status, ret);
// TODO: switch status and handle reasons here
status = R_DBG_REASON_UNKNOWN;
return status;
#endif
}
// TODO: why strdup here?
static const char *r_debug_native_reg_profile() {
#if __WINDOWS__
return strdup (
"=pc eip\n"
"=sp esp\n"
"=bp ebp\n"
"=a0 eax\n"
"=a1 ebx\n"
"=a2 ecx\n"
"=a3 edi\n"
"drx dr0 .32 4 0\n"
"drx dr1 .32 8 0\n"
"drx dr2 .32 12 0\n"
"drx dr3 .32 16 0\n"
"drx dr6 .32 20 0\n"
"drx dr7 .32 24 0\n"
/* floating save area 4+4+4+4+4+4+4+80+4 = 112 */
"seg gs .32 132 0\n"
"seg fs .32 136 0\n"
"seg es .32 140 0\n"
"seg ds .32 144 0\n"
"gpr edi .32 156 0\n"
"gpr esi .32 160 0\n"
"gpr ebx .32 164 0\n"
"gpr edx .32 168 0\n"
"gpr ecx .32 172 0\n"
"gpr eax .32 176 0\n"
"gpr ebp .32 180 0\n"
"gpr esp .32 196 0\n"
"gpr eip .32 184 0\n"
"seg cs .32 184 0\n"
"seg ds .32 152 0\n"
"seg gs .32 140 0\n"
"seg fs .32 144 0\n"
"gpr eflags .32 192 0 c1p.a.zstido.n.rv\n" // XXX must be flg
"seg ss .32 200 0\n"
/* +512 bytes for maximum supoprted extension extended registers */
);
#elif __POWERPC__ && __APPLE__
return strdup (
"=pc srr0\n"
"=sr srr1\n" // status register
"=a0 r0\n"
"=a1 r1\n"
"=a2 r2\n"
"=a3 r3\n"
#if 0
"=a4 r4\n"
"=a5 r5\n"
"=a6 r6\n"
"=a7 r7\n"
#endif
"gpr srr0 .32 0 0\n"
"gpr srr1 .32 4 0\n"
"gpr r0 .32 8 0\n"
"gpr r1 .32 12 0\n"
"gpr r2 .32 16 0\n"
"gpr r3 .32 20 0\n"
"gpr r4 .32 24 0\n"
"gpr r5 .32 28 0\n"
"gpr r6 .32 32 0\n"
"gpr r7 .32 36 0\n"
"gpr r8 .32 40 0\n"
"gpr r9 .32 44 0\n"
"gpr r10 .32 48 0\n"
"gpr r11 .32 52 0\n"
"gpr r12 .32 56 0\n"
"gpr r13 .32 60 0\n"
"gpr r14 .32 64 0\n"
"gpr r15 .32 68 0\n"
"gpr r16 .32 72 0\n"
"gpr r17 .32 76 0\n"
"gpr r18 .32 80 0\n"
"gpr r19 .32 84 0\n"
"gpr r20 .32 88 0\n"
"gpr r21 .32 92 0\n"
"gpr r22 .32 96 0\n"
"gpr r23 .32 100 0\n"
"gpr r24 .32 104 0\n"
"gpr r25 .32 108 0\n"
"gpr r26 .32 112 0\n"
"gpr r27 .32 116 0\n"
"gpr r28 .32 120 0\n"
"gpr r29 .32 124 0\n"
"gpr r30 .32 128 0\n"
"gpr r31 .32 132 0\n"
"gpr cr .32 136 0\n"
"gpr xer .32 140 0\n"
"gpr lr .32 144 0\n"
"gpr ctr .32 148 0\n"
"gpr mq .32 152 0\n"
"gpr vrsave .32 156 0\n"
);
#elif __i386__
return strdup (
"=pc eip\n"
"=sp esp\n"
"=bp ebp\n"
"=a0 eax\n"
"=a1 ebx\n"
"=a2 ecx\n"
"=a3 edi\n"
"gpr eip .32 48 0\n"
"gpr ip .16 48 0\n"
"gpr oeax .32 44 0\n"
"gpr eax .32 24 0\n"
"gpr ax .16 24 0\n"
"gpr ah .8 24 0\n"
"gpr al .8 25 0\n"
"gpr ebx .32 0 0\n"
"gpr bx .16 0 0\n"
"gpr bh .8 0 0\n"
"gpr bl .8 1 0\n"
"gpr ecx .32 4 0\n"
"gpr cx .16 4 0\n"
"gpr ch .8 4 0\n"
"gpr cl .8 5 0\n"
"gpr edx .32 8 0\n"
"gpr dx .16 8 0\n"
"gpr dh .8 8 0\n"
"gpr dl .8 9 0\n"
"gpr esp .32 60 0\n"
"gpr sp .16 60 0\n"
"gpr ebp .32 20 0\n"
"gpr bp .16 20 0\n"
"gpr esi .32 12 0\n"
"gpr si .16 12 0\n"
"gpr edi .32 16 0\n"
"gpr di .16 16 0\n"
"seg xfs .32 36 0\n"
"seg xgs .32 40 0\n"
"seg xcs .32 52 0\n"
"seg cs .16 52 0\n"
"seg xss .32 52 0\n"
"gpr eflags .32 56 0 c1p.a.zstido.n.rv\n"
"gpr flags .16 56 0\n"
"flg carry .1 .448 0\n"
"flg flag_p .1 .449 0\n"
"flg flag_a .1 .450 0\n"
"flg zero .1 .451 0\n"
"flg sign .1 .452 0\n"
"flg flag_t .1 .453 0\n"
"flg flag_i .1 .454 0\n"
"flg flag_d .1 .455 0\n"
"flg flag_o .1 .456 0\n"
"flg flag_r .1 .457 0\n"
"drx dr0 .32 0 0\n"
"drx dr1 .32 4 0\n"
"drx dr2 .32 8 0\n"
"drx dr3 .32 12 0\n"
//"drx dr4 .32 16 0\n"
//"drx dr5 .32 20 0\n"
"drx dr6 .32 24 0\n"
"drx dr7 .32 28 0\n"
);
#elif __x86_64__
return strdup (
"=pc rip\n"
"=sp rsp\n"
"=bp rbp\n"
"=a0 rax\n"
"=a1 rbx\n"
"=a2 rcx\n"
"=a3 rdx\n"
"# no profile defined for x86-64\n"
"gpr r15 .64 0 0\n"
"gpr r14 .64 8 0\n"
"gpr r13 .64 16 0\n"
"gpr r12 .64 24 0\n"
"gpr rbp .64 32 0\n"
"gpr rbx .64 40 0\n"
"gpr r11 .64 48 0\n"
"gpr r10 .64 56 0\n"
"gpr r9 .64 64 0\n"
"gpr r8 .64 72 0\n"
"gpr rax .64 80 0\n"
"gpr rcx .64 88 0\n"
"gpr rdx .64 96 0\n"
"gpr rsi .64 104 0\n"
"gpr rdi .64 112 0\n"
"gpr oeax .64 120 0\n"
"gpr rip .64 128 0\n"
"seg cs .64 136 0\n"
//"flg eflags .64 144 0\n"
"gpr eflags .32 144 0 c1p.a.zstido.n.rv\n"
"gpr rsp .64 152 0\n"
"seg ss .64 160 0\n"
"seg fs_base .64 168 0\n"
"seg gs_base .64 176 0\n"
"seg ds .64 184 0\n"
"seg es .64 192 0\n"
"seg fs .64 200 0\n"
"seg gs .64 208 0\n"
"drx dr0 .32 0 0\n"
"drx dr1 .32 4 0\n"
"drx dr2 .32 8 0\n"
"drx dr3 .32 12 0\n"
"drx dr6 .32 24 0\n"
"drx dr7 .32 28 0\n"
);
#elif __arm__
return strdup (
"=pc r15\n"
"=sp r14\n" // XXX
"=a0 r0\n"
"=a1 r1\n"
"=a2 r2\n"
"=a3 r3\n"
"gpr lr .32 56 0\n" // r14
"gpr pc .32 60 0\n" // r15
"gpr r0 .32 0 0\n"
"gpr r1 .32 4 0\n"
"gpr r2 .32 8 0\n"
"gpr r3 .32 12 0\n"
"gpr r4 .32 16 0\n"
"gpr r5 .32 20 0\n"
"gpr r6 .32 24 0\n"
"gpr r7 .32 28 0\n"
"gpr r8 .32 32 0\n"
"gpr r9 .32 36 0\n"
"gpr r10 .32 40 0\n"
"gpr r11 .32 44 0\n"
"gpr r12 .32 48 0\n"
"gpr r13 .32 52 0\n"
"gpr r14 .32 56 0\n"
"gpr r15 .32 60 0\n"
"gpr r16 .32 64 0\n"
"gpr r17 .32 68 0\n"
);
#else
#warning NO DEBUGGER REGISTERS PROFILE DEFINED
return NULL;
#endif
}
/*
TODO: list all pids in linux and bsd.. osx seems to be strange
int i;
for (i=1; i<9999; i++) {
if (!kill (i, 0))
r_list_append (list, r_debug_pid_new ("???", i, 's', 0));
}
*/
#if __APPLE__
// XXX
static RDebugPid *darwin_get_pid(int pid) {
int foo, nargs, mib[3];
size_t size, argmax = 2048;
char *curr_arg, *start_args, *iter_args, *end_args;
char *procargs = NULL;
char psname[4096];
#if 0
/* Get the maximum process arguments size. */
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
size = sizeof(argmax);
if (sysctl (mib, 2, &argmax, &size, NULL, 0) == -1) {
eprintf ("sysctl() error on getting argmax\n");
return NULL;
}
#endif
/* Allocate space for the arguments. */
procargs = (char *)malloc (argmax);
if (procargs == NULL) {
eprintf ("getcmdargs(): insufficient memory for procargs %p\n", argmax);
return NULL;
}
/*
* Make a sysctl() call to get the raw argument space of the process.
*/
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pid;
size = argmax;
procargs[0] = 0;
if (sysctl (mib, 3, procargs, &size, NULL, 0) == -1) {
if (EINVAL == errno) { // invalid == access denied for some reason
//eprintf("EINVAL returned fetching argument space\n");
free (procargs);
return NULL;
}
eprintf ("sysctl(): unspecified sysctl error - %i\n", errno);
free (procargs);
return NULL;
}
// copy the number of argument to nargs
memcpy (&nargs, procargs, sizeof(nargs));
iter_args = procargs + sizeof(nargs);
end_args = &procargs[size-30]; // end of the argument space
if (iter_args >= end_args) {
eprintf ("getcmdargs(): argument length mismatch");
free (procargs);
return NULL;
}
//TODO: save the environment variables to envlist as well
// Skip over the exec_path and '\0' characters.
// XXX: fix parsing
#if 0
while (iter_args < end_args && *iter_args != '\0') { iter_args++; }
while (iter_args < end_args && *iter_args == '\0') { iter_args++; }
#endif
if (iter_args == end_args) {
free (procargs);
return NULL;
}
/* Iterate through the '\0'-terminated strings and add each string
* to the Python List arglist as a Python string.
* Stop when nargs strings have been extracted. That should be all
* the arguments. The rest of the strings will be environment
* strings for the command.
*/
curr_arg = iter_args;
start_args = iter_args; //reset start position to beginning of cmdline
foo = 1;
psname[0] = 0;
while (iter_args < end_args && nargs > 0) {
if (*iter_args++ == '\0') {
if (foo) {
strcpy (psname, curr_arg);
foo = 0;
} else {
strcat (psname, " ");
strcat (psname, curr_arg);
}
//printf("arg[%i]: %s\n", iter_args, curr_arg);
/* Fetch next argument */
curr_arg = iter_args;
nargs--;
}
}
#if 1
/*
* curr_arg position should be further than the start of the argspace
* and number of arguments should be 0 after iterating above. Otherwise
* we had an empty argument space or a missing terminating \0 etc.
*/
if (curr_arg == start_args || nargs > 0) {
psname[0] = 0;
// eprintf ("getcmdargs(): argument parsing failed");
free (procargs);
return NULL;
}
#endif
return r_debug_pid_new (psname, pid, 's', 0); // XXX 's' ??, 0?? must set correct values
}
#endif
#undef MAXPID
#define MAXPID 69999
static RList *r_debug_native_pids(int pid) {
RList *list = r_list_new ();
#if __WINDOWS__
return w32_pids (pid, list);
#elif __APPLE__
if (pid) {
RDebugPid *p = darwin_get_pid (pid);
if (p) r_list_append (list, p);
} else {
int i;
for(i=1; i<MAXPID; i++) {
RDebugPid *p = darwin_get_pid (i);
if (p) r_list_append (list, p);
}
}
#else
int i, fd;
char *ptr, cmdline[1024];
// TODO: new syntax: R_LIST (r_debug_pid_free)
list->free = (RListFree)&r_debug_pid_free;
/* TODO */
if (pid) {
r_list_append (list, r_debug_pid_new ("(current)", pid, 's', 0));
/* list parents */
DIR *dh;
struct dirent *de;
dh = opendir ("/proc");
if (dh == NULL)
return NULL;
//for (i=2; i<39999; i++) {
while ((de = readdir (dh))) {
i = atoi (de->d_name); if (!i) continue;
snprintf (cmdline, sizeof (cmdline), "/proc/%d/status", i);
fd = open (cmdline, O_RDONLY);
if (fd == -1)
continue;
read (fd, cmdline, 1024);
cmdline[1023] = '\0';
ptr = strstr (cmdline, "PPid: ");
if (ptr) {
int ppid = atoi (ptr+6);
close (fd);
if (ppid != pid)
continue;
snprintf (cmdline, sizeof (cmdline), "/proc/%d/cmdline", ppid);
fd = open (cmdline, O_RDONLY);
if (fd == -1)
continue;
read (fd, cmdline, 1024);
cmdline[1023] = '\0';
r_list_append (list, r_debug_pid_new (cmdline, i, 's', 0));
}
close (fd);
}
closedir (dh);
} else
for (i=2; i<MAXPID; i++) {
if (!kill (i, 0)) {
// TODO: Use slurp!
snprintf (cmdline, sizeof (cmdline), "/proc/%d/cmdline", i);
fd = open (cmdline, O_RDONLY);
if (fd == -1)
continue;
cmdline[0] = '\0';
read (fd, cmdline, sizeof (cmdline));
cmdline[sizeof (cmdline)-1] = '\0';
close (fd);
r_list_append (list, r_debug_pid_new (cmdline, i, 's', 0));
}
}
#endif
return list;
}
static RList *r_debug_native_threads(int pid) {
RList *list = r_list_new ();
/* TODO */
#if __WINDOWS__
return w32_thread_list (pid, list);
#elif __APPLE__
#if __arm__
#define OSX_PC state.r15
#undef THREAD_STATE
#define THREAD_STATE ARM_THREAD_STATE
#elif __POWERPC__
#define OSX_PC state.srr0
#else
#define OSX_PC state.__eip
#endif
int i, tid, err;
unsigned int gp_count;
static thread_array_t inferior_threads = NULL;
static unsigned int inferior_thread_count = 0;
R_DEBUG_REG_T state;
if (task_threads (pid_to_task (pid), &inferior_threads,
&inferior_thread_count) != KERN_SUCCESS) {
eprintf ("Failed to get list of task's threads.\n");
return list;
}
for (i = 0; i < inferior_thread_count; i++) {
tid = inferior_threads[i];
gp_count = sizeof (R_DEBUG_REG_T);
if ((err = thread_get_state (tid, THREAD_STATE,
(thread_state_t) &state, &gp_count)) != KERN_SUCCESS) {
// eprintf ("debug_list_threads: %s\n", MACH_ERROR_STRING(err));
OSX_PC = 0;
}
r_list_append (list, r_debug_pid_new ("???", i, 's', OSX_PC));
}
#elif __linux__
int i, fd, thid = 0;
char *ptr, cmdline[1024];
if (!pid)
return NULL;
r_list_append (list, r_debug_pid_new ("(current)", pid, 's', 0));
/* list parents */
/* LOL! linux hides threads from /proc, but they are accessible!! HAHAHA */
//while ((de = readdir (dh))) {
snprintf (cmdline, sizeof (cmdline), "/proc/%d/task", pid);
if (r_file_exist (cmdline)) {
struct dirent *de;
DIR *dh = opendir (cmdline);
while ((de = readdir (dh))) {
int tid = atoi (de->d_name);
// TODO: get status, pc, etc..
r_list_append (list, r_debug_pid_new (cmdline, tid, 's', 0));
}
closedir (dh);
} else {
/* LOL! linux hides threads from /proc, but they are accessible!! HAHAHA */
//while ((de = readdir (dh))) {
for (i=pid; i<MAXPID; i++) { // XXX
snprintf (cmdline, sizeof (cmdline), "/proc/%d/status", i);
fd = open (cmdline, O_RDONLY);
if (fd == -1)
continue;
read (fd, cmdline, 1024);
close (fd);
cmdline[sizeof(cmdline)-1] = '\0';
ptr = strstr (cmdline, "Tgid:");
if (ptr) {
int tgid = atoi (ptr+5);
if (tgid != pid)
continue;
read (fd, cmdline, sizeof(cmdline)-1);
sprintf (cmdline, "thread_%d", thid++);
cmdline[sizeof(cmdline)-1] = '\0';
r_list_append (list, r_debug_pid_new (cmdline, i, 's', 0));
}
}
}
#else
eprintf ("TODO\n");
#endif
return list;
}
// TODO: what about float and hardware regs here ???
// TODO: add flag for type
static int r_debug_native_reg_read(RDebug *dbg, int type, ut8 *buf, int size) {
int pid = dbg->pid;
#if __WINDOWS__
CONTEXT ctx __attribute__ ((aligned (16)));
ctx.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
if (!GetThreadContext (tid2handler (dbg->pid, dbg->tid), &ctx)) {
eprintf ("GetThreadContext: %x\n", (int)GetLastError());
return R_FALSE;
}
if (sizeof (CONTEXT) < size)
size = sizeof (CONTEXT);
#if 0
// TODO: fix missing regs deltas in profile (DRX+..)
#include <r_util.h>
eprintf ("++ EAX = 0x%08x %d\n", ctx.Eax, r_offsetof (CONTEXT, Eax));
eprintf ("++ EBX = 0x%08x %d\n", ctx.Ebx, r_offsetof (CONTEXT, Ebx));
eprintf ("++ ECX = 0x%08x %d\n", ctx.Ecx, r_offsetof (CONTEXT, Ecx));
eprintf ("++ EDX = 0x%08x %d\n", ctx.Edx, r_offsetof (CONTEXT, Edx));
eprintf ("++ EIP = 0x%08x %d\n", ctx.Eip, r_offsetof (CONTEXT, Eip));
eprintf ("++ EDI = 0x%08x %d\n", ctx.Edi, r_offsetof (CONTEXT, Edi));
eprintf ("++ ESI = 0x%08x %d\n", ctx.Esi, r_offsetof (CONTEXT, Esi));
eprintf ("++ ESP = 0x%08x %d\n", ctx.Esp, r_offsetof (CONTEXT, Esp));
eprintf ("++ EBP = 0x%08x %d\n", ctx.Ebp, r_offsetof (CONTEXT, Ebp));
eprintf ("++ CS = 0x%08x %d\n", ctx.SegCs, r_offsetof (CONTEXT, SegCs));
eprintf ("++ DS = 0x%08x %d\n", ctx.SegDs, r_offsetof (CONTEXT, SegDs));
eprintf ("++ GS = 0x%08x %d\n", ctx.SegGs, r_offsetof (CONTEXT, SegGs));
eprintf ("++ FS = 0x%08x %d\n", ctx.SegFs, r_offsetof (CONTEXT, SegFs));
eprintf ("++ SS = 0x%08x %d\n", ctx.SegSs, r_offsetof (CONTEXT, SegSs));
eprintf ("++ EFL = 0x%08x %d\n", ctx.EFlags, r_offsetof (CONTEXT, EFlags));
#endif
memcpy (buf, &ctx, size);
return size;
// XXX this must be defined somewhere else
#elif __APPLE__
int ret;
thread_array_t inferior_threads = NULL;
unsigned int inferior_thread_count = 0;
R_DEBUG_REG_T *regs = (R_DEBUG_REG_T*)buf;
unsigned int gp_count = sizeof (R_DEBUG_REG_T);
if (size<sizeof(R_DEBUG_REG_T)) {
eprintf ("Small buffer passed to r_debug_read\n");
return R_FALSE;
}
ret = task_threads (pid_to_task (pid), &inferior_threads, &inferior_thread_count);
if (ret != KERN_SUCCESS) {
eprintf ("debug_getregs\n");
return R_FALSE;
}
if (inferior_thread_count>0) {
/* TODO: allow to choose the thread */
gp_count = sizeof (R_DEBUG_REG_T)/sizeof(size_t);
if (thread_get_state (inferior_threads[0], ARM_THREAD_STATE,
(thread_state_t) regs, &gp_count) != KERN_SUCCESS) {
eprintf ("debug_getregs: Failed to get thread %d %d.error (%x). (%s)\n",
(int)pid, pid_to_task (pid), (int)ret, MACH_ERROR_STRING (ret));
perror ("thread_get_state");
return R_FALSE;
}
} else eprintf ("There are no threads!\n");
return sizeof (R_DEBUG_REG_T);
#elif __linux__ || __sun || __NetBSD__ || __FreeBSD__ || __OpenBSD__
int ret;
switch (type) {
case R_REG_TYPE_DRX:
#ifdef __FreeBSD__
{
// TODO
struct dbreg dbr;
ret = ptrace (PTRACE_GETDBREGS, pid, &dbr, sizeof (dbr));
if (ret != 0)
return R_FALSE;
// XXX: maybe the register map is not correct, must review
}
#elif __linux__
{
int i;
for (i=0; i<7; i++) { // DR0-DR7
ret = ptrace (PTRACE_PEEKUSER, pid, r_offsetof (
struct user, u_debugreg[i]), 0);
if (ret != 0)
return R_FALSE;
memcpy (buf+(i*4), &ret, 4);
}
}
#else
return R_FALSE;
#endif
break;
case R_REG_TYPE_SEG:
case R_REG_TYPE_FLG:
case R_REG_TYPE_GPR:
{
R_DEBUG_REG_T regs;
memset (&regs, 0, sizeof (regs));
memset (buf, 0, size);
#if __NetBSD__ || __FreeBSD__ || __OpenBSD__
ret = ptrace (PTRACE_GETREGS, pid, &regs, sizeof (regs));
#elif __linux__ && __powerpc__
ret = ptrace (PTRACE_GETREGS, pid, &regs, NULL);
#else
/* linux/arm/x86/x64 */
ret = ptrace (PTRACE_GETREGS, pid, NULL, &regs);
#endif
if (ret != 0)
return R_FALSE;
if (sizeof (regs) < size)
size = sizeof (regs);
memcpy (buf, &regs, size);
return sizeof (regs);
}
break;
}
return R_TRUE;
#else
#warning dbg-native not supported for this platform
return R_FALSE;
#endif
}
static int r_debug_native_reg_write(int pid, int tid, int type, const ut8* buf, int size) {
// XXX use switch or so
if (type == R_REG_TYPE_DRX) {
#ifdef __FreeBSD__
return (0 == ptrace (PTRACE_SETDBREGS, dbg->pid, buf, sizeof (struct dbreg)));
#elif __linux__
{
int i;
ut32 *val = (ut32 *)buf;
for (i=0; i<7; i++) { // DR0-DR7
ptrace (PTRACE_POKEUSER, pid, r_offsetof (
struct user, u_debugreg[i]), *(val+i));
//if (ret != 0)
// return R_FALSE;
}
}
#else
eprintf ("TODO: add support for write DRX registers\n");
return R_FALSE;
#endif
} else
if (type == R_REG_TYPE_GPR) {
#if __WINDOWS__
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
return SetThreadContext (tid2handler (pid, tid), &ctx)? 0: -1;
#elif __linux__ || __sun || __NetBSD__ || __FreeBSD__ || __OpenBSD__
int ret = ptrace (PTRACE_SETREGS, pid, 0, buf);
if (sizeof (R_DEBUG_REG_T) < size)
size = sizeof (R_DEBUG_REG_T);
return (ret != 0) ? R_FALSE: R_TRUE;
#elif __APPLE__
int ret;
thread_array_t inferior_threads = NULL;
unsigned int inferior_thread_count = 0;
R_DEBUG_REG_T *regs = (R_DEBUG_REG_T*)buf;
unsigned int gp_count = sizeof (R_DEBUG_REG_T);
ret = task_threads (pid_to_task (pid), &inferior_threads, &inferior_thread_count);
if (ret != KERN_SUCCESS) {
eprintf ("debug_getregs\n");
return R_FALSE;
}
if (inferior_thread_count>0) {
/* TODO: allow to choose the thread */
gp_count = sizeof (R_DEBUG_REG_T)/sizeof(size_t);
if (thread_set_state (inferior_threads[0], ARM_THREAD_STATE,
(thread_state_t) regs, gp_count) != KERN_SUCCESS) {
eprintf ("debug_getregs: Failed to get thread %d %d.error (%x). (%s)\n",
(int)pid, pid_to_task (pid), (int)ret, MACH_ERROR_STRING (ret));
perror ("thread_get_state");
return R_FALSE;
}
} else eprintf ("There are no threads!\n");
return sizeof (R_DEBUG_REG_T);
#else
#warning r_debug_native_reg_write not implemented
#endif
} else eprintf ("TODO: reg_write_non-gpr (%d)\n", type);
return R_FALSE;
}
#if __APPLE__
static const char * unparse_inheritance (vm_inherit_t i) {
switch (i) {
case VM_INHERIT_SHARE: return "share";
case VM_INHERIT_COPY: return "copy";
case VM_INHERIT_NONE: return "none";
default: return "???";
}
}
// TODO: move to p/native/darwin.c
// TODO: this loop MUST be cleaned up
static RList *darwin_dbg_maps (RDebug *dbg) {
RDebugMap *mr;
RList *list = r_list_new ();
char buf[128];
int i, print;
kern_return_t kret;
vm_region_basic_info_data_64_t info, prev_info;
mach_vm_address_t prev_address;
mach_vm_size_t size, prev_size;
mach_port_t object_name;
mach_msg_type_number_t count;
int nsubregions = 0;
int num_printed = 0;
// XXX: wrong for 64bits
size_t address = 0;
task_t task = pid_to_task (dbg->pid);
/*
count = VM_REGION_BASIC_INFO_COUNT_64;
kret = mach_vm_region (pid_to_task (dbg->pid), &address, &size, VM_REGION_BASIC_INFO_64,
(vm_region_info_t) &info, &count, &object_name);
if (kret != KERN_SUCCESS) {
printf("No memory regions.\n");
return;
}
memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_64_t));
*/
size = 4096;
memset (&prev_info, 0, sizeof (prev_info));
prev_address = address;
prev_size = size;
nsubregions = 1;
for (i=0; ; i++) {
int done = 0;
address = prev_address + prev_size;
if (prev_size==0)
break;
/* Check to see if address space has wrapped around. */
if (address == 0)
done = 1;
if (!done) {
count = VM_REGION_BASIC_INFO_COUNT_64;
kret = mach_vm_region (task, (mach_vm_address_t *)&address,
&size, VM_REGION_BASIC_INFO_64,
(vm_region_info_t) &info, &count, &object_name);
if (kret != KERN_SUCCESS) {
size = 0;
print = done = 1;
}
}
if (address != prev_address + prev_size)
print = 1;
if ((info.protection != prev_info.protection)
|| (info.max_protection != prev_info.max_protection)
|| (info.inheritance != prev_info.inheritance)
|| (info.shared != prev_info.reserved)
|| (info.reserved != prev_info.reserved))
print = 1;
#define xwr2rwx(x) ((x&1)<<2) && (x&2) && ((x&4)>>2)
if (print) {
sprintf (buf, "%s %02x %s/%s/%s",
r_str_rwx_i (xwr2rwx (prev_info.max_protection)), i,
unparse_inheritance (prev_info.inheritance),
prev_info.shared ? "shar" : "priv",
prev_info.reserved ? "reserved" : "not-reserved");
// TODO: MAPS can have min and max protection rules
// :: prev_info.max_protection
mr = r_debug_map_new (buf, prev_address, prev_address+prev_size, prev_info.protection, 0);
if (mr == NULL) {
eprintf ("Cannot create r_debug_map_new\n");
break;
}
r_list_append (list, mr);
}
#if 0
if (1==0 && rest) { /* XXX never pritn this info here */
addr = 0LL;
addr = (ut64) (ut32) prev_address;
if (num_printed == 0)
fprintf(stderr, "Region ");
else fprintf(stderr, " ... ");
fprintf(stderr, " 0x%08llx - 0x%08llx %s (%s) %s, %s, %s",
addr, addr + prev_size,
unparse_protection (prev_info.protection),
unparse_protection (prev_info.max_protection),
unparse_inheritance (prev_info.inheritance),
prev_info.shared ? "shared" : " private",
prev_info.reserved ? "reserved" : "not-reserved");
if (nsubregions > 1)
fprintf(stderr, " (%d sub-regions)", nsubregions);
fprintf(stderr, "\n");
prev_address = address;
prev_size = size;
memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_64_t));
nsubregions = 1;
num_printed++;
} else {
#endif
#if 0
prev_size += size;
nsubregions++;
#else
prev_address = address;
prev_size = size;
memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_64_t));
nsubregions = 1;
num_printed++;
#endif
// }
}
return list;
}
#endif
static RList *r_debug_native_map_get(RDebug *dbg) {
RList *list = NULL;
#if __APPLE__
list = darwin_dbg_maps (dbg);
#elif __WINDOWS__
list = w32_dbg_maps (); // TODO: moar?
#else
#if __sun
char path[1024];
/* TODO: On solaris parse /proc/%d/map */
sprintf (path, "pmap %d > /dev/stderr", ps.tid);
system (path);
#else
RDebugMap *map;
int i, perm, unk = 0;
char *pos_c;
char path[1024], line[1024];
char region[100], region2[100], perms[5], null[16];
FILE *fd;
#if __FreeBSD__
sprintf (path, "/proc/%d/map", dbg->pid);
#else
sprintf (path, "/proc/%d/maps", dbg->pid);
#endif
fd = fopen (path, "r");
if (!fd) {
perror ("debug_init_maps: /proc");
return NULL;
}
list = r_list_new ();
while (!feof (fd)) {
line[0]='\0';
fgets (line, 1023, fd);
if (line[0]=='\0')
break;
path[0]='\0';
line[strlen (line)-1]='\0';
#if __FreeBSD__
// 0x8070000 0x8072000 2 0 0xc1fde948 rw- 1 0 0x2180 COW NC vnode /usr/bin/gcc
sscanf (line, "%s %s %d %d 0x%s %3s %d %d",
&region[2], &region2[2], &ign, &ign,
unkstr, perms, &ign, &ign);
pos_c = strchr (line, '/');
if (pos_c) strcpy (path, pos_c);
else path[0]='\0';
#else
sscanf (line, "%s %s %s %s %s %s",
&region[2], perms, null, null, null, path);
pos_c = strchr (&region[2], '-');
if (!pos_c)
continue;
pos_c[-1] = (char)'0';
pos_c[ 0] = (char)'x';
strcpy (region2, pos_c-1);
#endif // __FreeBSD__
region[0] = region2[0] = '0';
region[1] = region2[1] = 'x';
if (!*path)
sprintf (path, "unk%d", unk++);
perm = 0;
for(i = 0; perms[i] && i < 4; i++)
switch (perms[i]) {
case 'r': perm |= R_IO_READ; break;
case 'w': perm |= R_IO_WRITE; break;
case 'x': perm |= R_IO_EXEC; break;
}
map = r_debug_map_new (path,
r_num_get (NULL, region),
r_num_get (NULL, region2),
perm, 0);
if (map == NULL)
break;
#if 0
mr->ini = get_offset(region);
mr->end = get_offset(region2);
mr->size = mr->end - mr->ini;
mr->bin = strdup(path);
mr->perms = 0;
if(!strcmp(path, "[stack]") || !strcmp(path, "[vdso]"))
mr->flags = FLAG_NOPERM;
else
mr->flags = 0;
for(i = 0; perms[i] && i < 4; i++) {
switch(perms[i]) {
case 'r':
mr->perms |= REGION_READ;
break;
case 'w':
mr->perms |= REGION_WRITE;
break;
case 'x':
mr->perms |= REGION_EXEC;
}
}
#endif
r_list_append (list, map);
}
fclose (fd);
#endif // __sun
#endif // __WINDOWS
return list;
}
// TODO: deprecate???
#if 0
static int r_debug_native_bp_write(int pid, ut64 addr, int size, int hw, int rwx) {
if (hw) {
/* implement DRx register handling here */
return R_TRUE;
}
return R_FALSE;
}
/* TODO: rethink */
static int r_debug_native_bp_read(int pid, ut64 addr, int hw, int rwx) {
return R_TRUE;
}
#endif
#if __i386__
/* TODO: Can I use this as in a coroutine? */
static RList *r_debug_native_frames(RDebug *dbg) {
RRegItem *ri;
RReg *reg = dbg->reg;
ut32 i, _esp, esp, ebp2;
RList *list = r_list_new ();
RIOBind *bio = &dbg->iob;
ut8 buf[4];
list->free = free;
ri = r_reg_get (reg, "esp", R_REG_TYPE_GPR);
if (ri != NULL) {
_esp = r_reg_get_value (reg, ri);
// TODO: implement [stack] map uptrace method too
esp = _esp;
for (i=0; i<MAXBT; i++) {
bio->read_at (bio->io, esp, (void *)&ebp2, 4);
*buf = '\0';
bio->read_at (bio->io, (ebp2-5)-(ebp2-5)%4, (void *)&buf, 4);
// TODO: arch_is_call() here and this fun will be portable
if (buf[(ebp2-5)%4]==0xe8) {
RDebugFrame *frame = R_NEW (RDebugFrame);
frame->addr = ebp2;
frame->size = esp-_esp;
r_list_append (list, frame);
}
esp += 4;
}
}
return list;
}
#elif __x86_64__
// XXX: Do this work correctly?
static RList *r_debug_native_frames(RDebug *dbg) {
int i;
ut8 buf[4];
ut64 ptr, ebp2;
ut64 _rip, _rsp, _rbp;
RList *list;
RReg *reg = dbg->reg;
RIOBind *bio = &dbg->iob;
_rip = r_reg_get_value (reg, r_reg_get (reg, "rip", R_REG_TYPE_GPR));
_rsp = r_reg_get_value (reg, r_reg_get (reg, "rsp", R_REG_TYPE_GPR));
_rbp = r_reg_get_value (reg, r_reg_get (reg, "rbp", R_REG_TYPE_GPR));
list = r_list_new ();
list->free = free;
bio->read_at (bio->io, _rip, &buf, 4);
/* %rbp=old rbp, %rbp+4 points to ret */
/* Plugin before function prelude: push %rbp ; mov %rsp, %rbp */
if (!memcmp (buf, "\x55\x89\xe5", 3) || !memcmp (buf, "\x89\xe5\x57", 3)) {
if (bio->read_at (bio->io, _rsp, &ptr, 4) != 4) {
eprintf ("read error at 0x%08"PFMT64x"\n", _rsp);
return R_FALSE;
}
RDebugFrame *frame = R_NEW (RDebugFrame);
frame->addr = ptr;
frame->size = 0; // TODO ?
r_list_append (list, frame);
_rbp = ptr;
}
for (i=1; i<MAXBT; i++) {
// TODO: make those two reads in a shot
bio->read_at (bio->io, _rbp, &ebp2, 4);
bio->read_at (bio->io, _rbp+4, &ptr, 4);
if (!ptr || !_rbp)
break;
RDebugFrame *frame = R_NEW (RDebugFrame);
frame->addr = ptr;
frame->size = 0; // TODO ?
r_list_append (list, frame);
_rbp = ebp2;
}
return list;
}
#else
#warning Backtrace frames not implemented for this platform
static RList *r_debug_native_frames(RDebug *dbg) {
return NULL;
}
#endif
// TODO: implement own-defined signals
static int r_debug_native_kill(RDebug *dbg, boolt thread, int sig) {
#if __WINDOWS__
// TODO: implement thread support signaling here
HANDLE hProcess; // XXX
#if 0
static uint WM_CLOSE = 0x10;
static bool CloseWindow(IntPtr hWnd) {
hWnd = FindWindowByCaption (0, "explorer");
SendMessage(hWnd, WM_CLOSE, NULL, NULL);
CloseWindow(hWnd);
return true;
}
#endif
TerminateProcess (hProcess, 1);
return R_FALSE;
#else
int ret = R_FALSE;
if (thread) {
#if 0
// XXX this is linux>2.5 specific..ugly
if (dbg->tid>0 && (ret = tgkill (dbg->pid, dbg->tid, sig))) {
if (ret != -1)
ret = R_TRUE;
}
#endif
} else {
if (dbg->pid>0 && (ret = kill (dbg->pid, sig))) {
if (ret != -1)
ret = R_TRUE;
}
}
return ret;
#endif
}
static int r_debug_native_init(RDebug *dbg) {
#if __WINDOWS__
return w32_dbg_init ();
#else
return R_TRUE;
#endif
}
#if __i386__ || __x86_64__
int drx_add(RDebug *dbg, ut64 addr, int rwx) {
// TODO
return R_FALSE;
}
int drx_del(RDebug *dbg, ut64 addr, int rwx) {
// TODO
return R_FALSE;
}
#endif
static int r_debug_native_bp(void *user, int add, ut64 addr, int hw, int rwx) {
#if __i386__ || __x86_64__
RDebug *dbg = user;
if (hw) {
if (add) return drx_add (dbg, addr, rwx);
return drx_del (dbg, addr, rwx);
}
#endif
return R_FALSE;
}
struct r_debug_plugin_t r_debug_plugin_native = {
.name = "native",
#if __i386__
.bits = R_SYS_BITS_32,
.arch = R_ASM_ARCH_X86,
#elif __x86_64__
.bits = R_SYS_BITS_32 | R_SYS_BITS_64,
.arch = R_ASM_ARCH_X86,
#elif __arm__
.bits = R_SYS_BITS_32,
.arch = R_ASM_ARCH_ARM,
#elif __mips__
.bits = R_SYS_BITS_32,
.arch = R_ASM_ARCH_MIPS,
#elif __powerpc__
.bits = R_SYS_BITS_32,
.arch = R_ASM_ARCH_PPC,
#else
#warning Unsupported architecture
#endif
.init = &r_debug_native_init,
.step = &r_debug_native_step,
.cont = &r_debug_native_continue,
.contsc = &r_debug_native_continue_syscall,
.attach = &r_debug_native_attach,
.detach = &r_debug_native_detach,
.pids = &r_debug_native_pids,
.threads = &r_debug_native_threads,
.wait = &r_debug_native_wait,
.kill = &r_debug_native_kill,
.frames = &r_debug_native_frames,
.reg_profile = (void *)&r_debug_native_reg_profile,
.reg_read = &r_debug_native_reg_read,
.reg_write = (void *)&r_debug_native_reg_write,
.map_get = (void *)&r_debug_native_map_get,
.breakpoint = r_debug_native_bp,
};
#ifndef CORELIB
struct r_lib_struct_t radare_plugin = {
.type = R_LIB_TYPE_DBG,
.data = &r_debug_plugin_native
};
#endif // CORELIB
//#endif
#else // DEBUGGER
struct r_debug_plugin_t r_debug_plugin_native = {
.name = "native",
};
#endif // DEBUGGER