mirror of
https://github.com/radareorg/radare2.git
synced 2025-01-13 09:11:49 +00:00
* Fix xnu_wait stopping prematurely (#7744) * Use task_swap_exception_ports (instead of *get* then *set*) * Add new debugger stop reason for user-initiated breaks * Stop the inferior when `dw` is interrupted (#7744) * Stop the inferior after attaching
This commit is contained in:
parent
f0faf8bbea
commit
6ae9d891db
@ -555,6 +555,7 @@ R_API const char *r_debug_reason_to_string(int type) {
|
||||
case R_DEBUG_REASON_INT: return "interrupt";
|
||||
case R_DEBUG_REASON_FPU: return "fpu";
|
||||
case R_DEBUG_REASON_STEP: return "step";
|
||||
case R_DEBUG_REASON_USERSUSP: return "suspended-by-user";
|
||||
}
|
||||
return "unhandled";
|
||||
}
|
||||
|
@ -355,8 +355,24 @@ static RDebugReasonType r_debug_native_wait (RDebug *dbg, int pid) {
|
||||
}
|
||||
|
||||
#if __APPLE__
|
||||
// eprintf ("No waitpid here :D\n");
|
||||
reason = xnu_wait (dbg, pid);
|
||||
r_cons_break_push (NULL, NULL);
|
||||
do {
|
||||
reason = xnu_wait (dbg, pid);
|
||||
if (reason == R_DEBUG_REASON_MACH_RCV_INTERRUPTED) {
|
||||
if (r_cons_is_breaked ()) {
|
||||
// Perhaps check the inferior is still alive,
|
||||
// otherwise xnu_stop will fail.
|
||||
reason = xnu_stop (dbg, pid)
|
||||
? R_DEBUG_REASON_USERSUSP
|
||||
: R_DEBUG_REASON_UNKNOWN;
|
||||
} else {
|
||||
// Weird; we'll retry the wait.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} while (true);
|
||||
r_cons_break_pop ();
|
||||
status = reason? 1: 0;
|
||||
#else
|
||||
#if __linux__ && !defined (WAIT_ON_ALL_CHILDREN)
|
||||
|
@ -173,6 +173,7 @@ int xnu_attach(RDebug *dbg, int pid) {
|
||||
eprintf ("error setting up exception thread\n");
|
||||
return -1;
|
||||
}
|
||||
xnu_stop (dbg, pid);
|
||||
return pid;
|
||||
#endif
|
||||
}
|
||||
@ -198,6 +199,60 @@ int xnu_detach(RDebug *dbg, int pid) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static int task_suspend_count(task_t task) {
|
||||
kern_return_t kr;
|
||||
struct task_basic_info info;
|
||||
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
|
||||
kr = task_info (task, TASK_BASIC_INFO, (task_info_t) &info, &count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
eprintf ("failed to get task info\n");
|
||||
return -1;
|
||||
}
|
||||
return info.suspend_count;
|
||||
}
|
||||
|
||||
int xnu_stop(RDebug *dbg, int pid) {
|
||||
#if XNU_USE_PTRACE
|
||||
eprintf ("xnu_stop: not implemented\n");
|
||||
return false;
|
||||
#else
|
||||
kern_return_t kr;
|
||||
task_t task;
|
||||
int suspend_count;
|
||||
|
||||
task = pid_to_task (pid);
|
||||
if (!task) {
|
||||
return false;
|
||||
}
|
||||
|
||||
suspend_count = task_suspend_count (task);
|
||||
if (suspend_count == -1) {
|
||||
return false;
|
||||
}
|
||||
if (suspend_count == 1) {
|
||||
// Hopefully _we_ suspended it.
|
||||
return true;
|
||||
}
|
||||
if (suspend_count > 1) {
|
||||
// This is unexpected.
|
||||
return false;
|
||||
}
|
||||
|
||||
kr = task_suspend (task);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
eprintf ("failed to suspend task\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
suspend_count = task_suspend_count (task);
|
||||
if (suspend_count != 1) {
|
||||
// This is unexpected.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
int xnu_continue(RDebug *dbg, int pid, int tid, int sig) {
|
||||
#if XNU_USE_PTRACE
|
||||
void *data = (void*)(size_t)((sig != -1) ? sig : dbg->reason.signum);
|
||||
|
@ -250,6 +250,7 @@ static int coredump_nflavors = 1;
|
||||
|
||||
#define MAX_TSTATE_FLAVORS 10
|
||||
#define DEFAULT_COREFILE_DEST "core.%u"
|
||||
#define R_DEBUG_REASON_MACH_RCV_INTERRUPTED -2
|
||||
|
||||
typedef struct {
|
||||
vm_offset_t header;
|
||||
@ -268,6 +269,7 @@ const char *xnu_reg_profile (RDebug *dbg);
|
||||
int xnu_attach (RDebug *dbg, int pid);
|
||||
bool xnu_step (RDebug *dbg);
|
||||
int xnu_detach (RDebug *dbg, int pid);
|
||||
int xnu_stop (RDebug *dbg, int pid);
|
||||
int xnu_continue (RDebug *dbg, int pid, int tid, int sig);
|
||||
RDebugMap *xnu_map_alloc (RDebug *dbg, ut64 addr, int size);
|
||||
int xnu_map_dealloc (RDebug *dbg, ut64 addr, int size);
|
||||
|
@ -199,20 +199,9 @@ static int modify_trace_bit(RDebug *dbg, xnu_thread *th, int enable) {
|
||||
#error "unknown architecture"
|
||||
#endif
|
||||
|
||||
// TODO: Tuck this into RDebug; `void *user` seems like a good candidate.
|
||||
static xnu_exception_info ex = { { 0 } };
|
||||
|
||||
static bool xnu_save_exception_ports (int pid) {
|
||||
kern_return_t kr;
|
||||
task_t task = pid_to_task (pid);
|
||||
if (!task)
|
||||
return false;
|
||||
ex.count = (sizeof (ex.ports) / sizeof (ex.ports[0]));
|
||||
kr = task_get_exception_ports (task, EXC_MASK_ALL,
|
||||
ex.masks, &ex.count, ex.ports,
|
||||
ex.behaviors, ex.flavors);
|
||||
return (kr == KERN_SUCCESS);
|
||||
}
|
||||
|
||||
static bool xnu_restore_exception_ports (int pid) {
|
||||
kern_return_t kr;
|
||||
int i;
|
||||
@ -372,12 +361,11 @@ static int __xnu_wait (RDebug *dbg, int pid) {
|
||||
for (;;) {
|
||||
kr = mach_msg (
|
||||
&msg.hdr,
|
||||
MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
|
||||
sizeof (exc_msg), ex.exception_port, 10, MACH_PORT_NULL);
|
||||
if (kr == MACH_RCV_INTERRUPTED ) {
|
||||
eprintf ("message interrupted\n");
|
||||
break;
|
||||
} else if (kr == MACH_RCV_TIMED_OUT) {
|
||||
MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0,
|
||||
sizeof (exc_msg), ex.exception_port,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (kr == MACH_RCV_INTERRUPTED) {
|
||||
reason = R_DEBUG_REASON_MACH_RCV_INTERRUPTED;
|
||||
break;
|
||||
} else if (kr != MACH_MSG_SUCCESS) {
|
||||
eprintf ("message didn't succeded\n");
|
||||
@ -428,7 +416,6 @@ bool xnu_create_exception_thread(RDebug *dbg) {
|
||||
return false;
|
||||
#else
|
||||
kern_return_t kr;
|
||||
bool ret;
|
||||
mach_port_t exception_port = MACH_PORT_NULL;
|
||||
mach_port_t req_port;
|
||||
// Got the mach port for the current process
|
||||
@ -452,17 +439,13 @@ bool xnu_create_exception_thread(RDebug *dbg) {
|
||||
kr = mach_port_insert_right (task_self, exception_port, exception_port,
|
||||
MACH_MSG_TYPE_MAKE_SEND);
|
||||
RETURN_ON_MACH_ERROR ("error to allocate insert right\n", false);
|
||||
// Save the original state of the exception ports for our child process
|
||||
ret = xnu_save_exception_ports (dbg->pid);
|
||||
if (!ret) {
|
||||
eprintf ("error to save exception port info\n");
|
||||
return false;
|
||||
}
|
||||
// Set the ability to get all exceptions on this port
|
||||
kr = task_set_exception_ports (task, EXC_MASK_ALL, exception_port,
|
||||
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
|
||||
THREAD_STATE_NONE);
|
||||
RETURN_ON_MACH_ERROR ("error to set port to receive exceptions\n", false);
|
||||
// Atomically swap out (and save) the child process's exception ports
|
||||
// for the one we just created. We'll want to receive all exceptions.
|
||||
ex.count = (sizeof (ex.ports) / sizeof (*ex.ports));
|
||||
kr = task_swap_exception_ports (task, EXC_MASK_ALL, exception_port,
|
||||
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
|
||||
ex.masks, &ex.count, ex.ports, ex.behaviors, ex.flavors);
|
||||
RETURN_ON_MACH_ERROR ("failed to swap exception ports\n", false);
|
||||
//get notification when process die
|
||||
kr = mach_port_request_notification (task_self, pid_to_task (dbg->pid),
|
||||
MACH_NOTIFY_DEAD_NAME, 0,
|
||||
|
@ -108,6 +108,7 @@ typedef enum {
|
||||
R_DEBUG_REASON_SWI,
|
||||
R_DEBUG_REASON_INT,
|
||||
R_DEBUG_REASON_FPU,
|
||||
R_DEBUG_REASON_USERSUSP,
|
||||
} RDebugReasonType;
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user