static void event_pid_stopped(pid_t pid, int signal, int event, int syscall) { int stopped_by_exception = 0; unsigned long msg = 0; Context * ctx = NULL; Context * ctx2 = NULL; trace(LOG_EVENTS, "event: pid %d stopped, signal %d", pid, signal); ctx = context_find_from_pid(pid, 1); if (ctx == NULL) { ctx = find_pending(pid); if (ctx != NULL) { Context * prs = ctx; assert(prs->ref_count == 0); ctx = create_context(pid2id(pid, pid)); EXT(ctx)->pid = pid; EXT(ctx)->regs = (REG_SET *)loc_alloc(sizeof(REG_SET)); ctx->pending_intercept = 1; ctx->mem = prs; ctx->parent = prs; ctx->big_endian = prs->big_endian; prs->ref_count++; list_add_last(&ctx->cldl, &prs->children); link_context(prs); link_context(ctx); send_context_created_event(prs); send_context_created_event(ctx); if (EXT(prs)->attach_callback) { EXT(prs)->attach_callback(0, prs, EXT(prs)->attach_data); EXT(prs)->attach_callback = NULL; EXT(prs)->attach_data = NULL; } } } if (ctx == NULL) return; assert(!ctx->exited); assert(!EXT(ctx)->attach_callback); if (signal != SIGSTOP && signal != SIGTRAP) { sigset_set(&ctx->pending_signals, signal, 1); if (sigset_get(&ctx->sig_dont_stop, signal) == 0) { ctx->pending_intercept = 1; stopped_by_exception = 1; } } if (ctx->stopped) { send_context_changed_event(ctx); } else { thread_state_t state; unsigned int state_count; ContextAddress pc0 = 0; ContextAddress pc1 = 0; assert(!EXT(ctx)->regs_dirty); EXT(ctx)->end_of_step = 0; EXT(ctx)->ptrace_event = event; ctx->signal = signal; ctx->stopped_by_bp = 0; ctx->stopped_by_exception = stopped_by_exception; ctx->stopped = 1; if (EXT(ctx)->regs_error) { release_error_report(EXT(ctx)->regs_error); EXT(ctx)->regs_error = NULL; } else { pc0 = get_regs_PC(ctx); } if (thread_get_state(EXT(ctx)->pid, x86_THREAD_STATE32, EXT(ctx)->regs, &state_count) != KERN_SUCCESS) { assert(errno != 0); EXT(ctx)->regs_error = get_error_report(errno); trace(LOG_ALWAYS, "error: thread_get_state failed; id %s, error %d %s", ctx->id, errno, errno_to_str(errno)); } else { pc1 = get_regs_PC(ctx); } if (!EXT(ctx)->syscall_enter || EXT(ctx)->regs_error || pc0 != pc1) { EXT(ctx)->syscall_enter = 0; EXT(ctx)->syscall_exit = 0; EXT(ctx)->syscall_id = 0; EXT(ctx)->syscall_pc = 0; } trace(LOG_EVENTS, "event: pid %d stopped at PC = %#lx", pid, pc1); if (signal == SIGTRAP && event == 0 && !syscall) { size_t break_size = 0; get_break_instruction(ctx, &break_size); ctx->stopped_by_bp = !EXT(ctx)->regs_error && is_breakpoint_address(ctx, pc1 - break_size); EXT(ctx)->end_of_step = !ctx->stopped_by_bp && EXT(ctx)->pending_step; if (ctx->stopped_by_bp) set_regs_PC(ctx, pc1 - break_size); } EXT(ctx)->pending_step = 0; send_context_stopped_event(ctx); } }
static void event_pid_stopped(pid_t pid, int signal, int event, int syscall) { int stopped_by_exception = 0; Context * ctx = NULL; trace(LOG_EVENTS, "event: pid %d stopped, signal %d, event %s", pid, signal, event_name(event)); ctx = context_find_from_pid(pid, 1); if (ctx == NULL) { ctx = find_pending(pid); if (ctx != NULL) { Context * prs = ctx; assert(prs->ref_count == 0); ctx = create_context(pid2id(pid, pid)); EXT(ctx)->pid = pid; EXT(ctx)->regs = (REG_SET *)loc_alloc(sizeof(REG_SET)); ctx->pending_intercept = 1; ctx->mem = prs; ctx->parent = prs; ctx->big_endian = prs->big_endian; prs->ref_count++; list_add_last(&ctx->cldl, &prs->children); link_context(prs); link_context(ctx); send_context_created_event(prs); send_context_created_event(ctx); if (EXT(prs)->attach_callback) { EXT(prs)->attach_callback(0, prs, EXT(prs)->attach_data); EXT(prs)->attach_callback = NULL; EXT(prs)->attach_data = NULL; } } } if (ctx == NULL) return; assert(!ctx->exited); assert(!EXT(ctx)->attach_callback); if (signal != SIGSTOP && signal != SIGTRAP) { assert(signal < 32); ctx->pending_signals |= 1 << signal; if ((ctx->sig_dont_stop & (1 << signal)) == 0) { ctx->pending_intercept = 1; stopped_by_exception = 1; } } if (ctx->stopped) { send_context_changed_event(ctx); } else { ContextAddress pc0 = 0; ContextAddress pc1 = 0; assert(!EXT(ctx)->regs_dirty); EXT(ctx)->end_of_step = 0; EXT(ctx)->ptrace_event = event; ctx->signal = signal; ctx->stopped_by_bp = 0; ctx->stopped_by_exception = stopped_by_exception; ctx->stopped = 1; if (EXT(ctx)->regs_error) { release_error_report(EXT(ctx)->regs_error); EXT(ctx)->regs_error = NULL; } else { pc0 = get_regs_PC(ctx); } if (ptrace(PTRACE_GETREGS, EXT(ctx)->pid, 0, (int)EXT(ctx)->regs) < 0) { assert(errno != 0); if (errno == ESRCH) { /* Racing condition: somebody resumed this context while we are handling stop event. * * One possible cause: main thread has exited forcing children to exit too. * I beleive it is a bug in PTRACE implementation - PTRACE should delay exiting of * a context while it is stopped, but it does not, which causes a nasty racing. * * Workaround: Ignore current event, assume context is running. */ ctx->stopped = 0; return; } EXT(ctx)->regs_error = get_error_report(errno); trace(LOG_ALWAYS, "error: ptrace(PTRACE_GETREGS) failed; id %s, error %d %s", ctx->id, errno, errno_to_str(errno)); } else { pc1 = get_regs_PC(ctx); } trace(LOG_EVENTS, "event: pid %d stopped at PC = %#lx", pid, pc1); if (signal == SIGTRAP && event == 0 && !syscall) { size_t break_size = 0; get_break_instruction(ctx, &break_size); ctx->stopped_by_bp = !EXT(ctx)->regs_error && is_breakpoint_address(ctx, pc1 - break_size); EXT(ctx)->end_of_step = !ctx->stopped_by_bp && EXT(ctx)->pending_step; if (ctx->stopped_by_bp) set_regs_PC(ctx, pc1 - break_size); } EXT(ctx)->pending_step = 0; send_context_stopped_event(ctx); } }