/* The source code for Apple's GDB was used as a reference for the exception forwarding code. This code is similar to the GDB code only because there is only one way to do it. */ static kern_return_t forward_exception( mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t data, mach_msg_type_number_t data_count ) { int i; kern_return_t r; mach_port_t port; exception_behavior_t behavior; thread_state_flavor_t flavor; thread_state_data_t thread_state; mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; for(i=0;i<old_exc_ports.count;i++) if(old_exc_ports.masks[i] & (1 << exception)) break; if(i==old_exc_ports.count) ABORT("No handler for exception!"); port = old_exc_ports.ports[i]; behavior = old_exc_ports.behaviors[i]; flavor = old_exc_ports.flavors[i]; if(behavior != EXCEPTION_DEFAULT) { r = thread_get_state(thread,flavor,thread_state,&thread_state_count); if(r != KERN_SUCCESS) ABORT("thread_get_state failed in forward_exception"); } switch(behavior) { case EXCEPTION_DEFAULT: r = exception_raise(port,thread,task,exception,data,data_count); break; case EXCEPTION_STATE: r = exception_raise_state(port,thread,task,exception,data, data_count,&flavor,thread_state,thread_state_count, thread_state,&thread_state_count); break; case EXCEPTION_STATE_IDENTITY: r = exception_raise_state_identity(port,thread,task,exception,data, data_count,&flavor,thread_state,thread_state_count, thread_state,&thread_state_count); break; default: r = KERN_FAILURE; /* make gcc happy */ ABORT("forward_exception: unknown behavior"); break; } if(behavior != EXCEPTION_DEFAULT) { r = thread_set_state(thread,flavor,thread_state,thread_state_count); if(r != KERN_SUCCESS) ABORT("thread_set_state failed in forward_exception"); } return r; }
void osfmach3_trap_forward( exception_type_t exc_type, exception_data_t code, mach_msg_type_number_t code_count, int *flavor, thread_state_t old_state, mach_msg_type_number_t *icnt, thread_state_t new_state, mach_msg_type_number_t *ocnt) { kern_return_t kr; mach_msg_type_number_t exc_count; exception_mask_t exc_mask; mach_port_t exc_port; exception_behavior_t exc_behavior; thread_state_flavor_t exc_flavor; mach_port_t thread_port, task_port; /* * Check if a debugger has changed the task exception port: * if so, forward the exception to the debugger. */ exc_port = MACH_PORT_NULL; task_port = current->osfmach3.task->mach_task_port; thread_port = current->osfmach3.thread->mach_thread_port; exc_count = 1; kr = task_get_exception_ports(task_port, 1 << exc_type, &exc_mask, &exc_count, &exc_port, &exc_behavior, &exc_flavor); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_forward: " "task_get_exception_ports(0x%x, 0x%x)", task_port, 1 << exc_type)); current->osfmach3.thread->exception_completed = FALSE; return; } if (!MACH_PORT_VALID(exc_port)) { current->osfmach3.thread->exception_completed = FALSE; return; } ASSERT(exc_mask == 1 << exc_type); if (exc_port == user_trap_port) { /* * Nothing has changed. Process the exception normally. */ if (user_trap_port_refs++ >= 0x7000) { kr = mach_port_mod_refs(mach_task_self(), exc_port, MACH_PORT_RIGHT_SEND, -0x7000); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_forward: " "mach_port_mod_refs(0x%x)", exc_port)); } user_trap_port_refs -= 0x7000; } current->osfmach3.thread->exception_completed = FALSE; return; } /* * A debugger has changed the exception port because it expects * to intercept this type of exception. Forward the exception. */ switch (exc_behavior) { case EXCEPTION_DEFAULT: current->osfmach3.thread->exception_completed = TRUE; server_thread_blocking(FALSE); kr = exception_raise(exc_port, thread_port, task_port, exc_type, code, code_count); server_thread_unblocking(FALSE); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_forward: " "exception_raise(0x%x, 0x%x)", exc_port, exc_type)); break; } server_thread_blocking(FALSE); kr = thread_get_state(thread_port, *flavor, old_state, icnt); server_thread_unblocking(FALSE); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_forward: " "thread_get_state(0x%x) [1]", thread_port)); } break; case EXCEPTION_STATE: if (exc_flavor != *flavor) { printk("osfmach3_trap_forward: " "can't forward exception_state - " "got flavor 0x%x, want 0x%x\n", *flavor, exc_flavor); current->osfmach3.thread->exception_completed = FALSE; break; } current->osfmach3.thread->exception_completed = TRUE; server_thread_blocking(FALSE); kr = exception_raise_state(exc_port, exc_type, code, code_count, flavor, old_state, *icnt, new_state, ocnt); server_thread_unblocking(FALSE); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_forward: " "exception_raise_state(0x%x, 0x%x)", exc_port, exc_type)); } break; case EXCEPTION_STATE_IDENTITY: if (exc_flavor != *flavor) { printk("osfmach3_trap_forward: " "can't forward exception_state_identity - " "got flavor 0x%x, want 0x%x\n", *flavor, exc_flavor); current->osfmach3.thread->exception_completed = FALSE; break; } current->osfmach3.thread->exception_completed = TRUE; server_thread_blocking(FALSE); kr = exception_raise_state_identity(exc_port, thread_port, task_port, exc_type, code, code_count, flavor, old_state, *icnt, new_state, ocnt); server_thread_unblocking(FALSE); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_forward: " "exception_raise_state_identity" "(0x%x, 0x%x)", exc_port, exc_type)); } break; default: printk("osfmach3_trap_forward: " "unknown behavior 0x%x for task exception 0x%x\n", exc_behavior, exc_type); current->osfmach3.thread->exception_completed = FALSE; break; } if (exc_port != MACH_PORT_NULL) { kr = mach_port_deallocate(mach_task_self(), exc_port); if (kr != KERN_SUCCESS) { MACH3_DEBUG(1, kr, ("osfmach3_trap_forward: " "mach_port_deallocate(0x%x)", exc_port)); } } }
/* * Routine: exception_deliver * Purpose: * Make an upcall to the exception server provided. * Conditions: * Nothing locked and no resources held. * Called from an exception context, so * thread_exception_return and thread_kdb_return * are possible. * Returns: * KERN_SUCCESS if the exception was handled */ kern_return_t exception_deliver( thread_t thread, exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt, struct exception_action *excp, lck_mtx_t *mutex) { ipc_port_t exc_port; exception_data_type_t small_code[EXCEPTION_CODE_MAX]; int code64; int behavior; int flavor; kern_return_t kr; /* * Save work if we are terminating. * Just go back to our AST handler. */ if (!thread->active) return KERN_SUCCESS; /* * Snapshot the exception action data under lock for consistency. * Hold a reference to the port over the exception_raise_* calls * so it can't be destroyed. This seems like overkill, but keeps * the port from disappearing between now and when * ipc_object_copyin_from_kernel is finally called. */ lck_mtx_lock(mutex); exc_port = excp->port; if (!IP_VALID(exc_port)) { lck_mtx_unlock(mutex); return KERN_FAILURE; } ip_lock(exc_port); if (!ip_active(exc_port)) { ip_unlock(exc_port); lck_mtx_unlock(mutex); return KERN_FAILURE; } ip_reference(exc_port); exc_port->ip_srights++; ip_unlock(exc_port); flavor = excp->flavor; behavior = excp->behavior; lck_mtx_unlock(mutex); code64 = (behavior & MACH_EXCEPTION_CODES); behavior &= ~MACH_EXCEPTION_CODES; if (!code64) { small_code[0] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[0]); small_code[1] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[1]); } switch (behavior) { case EXCEPTION_STATE: { mach_msg_type_number_t state_cnt; thread_state_data_t state; c_thr_exc_raise_state++; state_cnt = _MachineStateCount[flavor]; kr = thread_getstatus(thread, flavor, (thread_state_t)state, &state_cnt); if (kr == KERN_SUCCESS) { if (code64) { kr = mach_exception_raise_state(exc_port, exception, code, codeCnt, &flavor, state, state_cnt, state, &state_cnt); } else { kr = exception_raise_state(exc_port, exception, small_code, codeCnt, &flavor, state, state_cnt, state, &state_cnt); } if (kr == MACH_MSG_SUCCESS) kr = thread_setstatus(thread, flavor, (thread_state_t)state, state_cnt); } return kr; } case EXCEPTION_DEFAULT: c_thr_exc_raise++; if (code64) { kr = mach_exception_raise(exc_port, retrieve_thread_self_fast(thread), retrieve_task_self_fast(thread->task), exception, code, codeCnt); } else { kr = exception_raise(exc_port, retrieve_thread_self_fast(thread), retrieve_task_self_fast(thread->task), exception, small_code, codeCnt); } return kr; case EXCEPTION_STATE_IDENTITY: { mach_msg_type_number_t state_cnt; thread_state_data_t state; c_thr_exc_raise_state_id++; state_cnt = _MachineStateCount[flavor]; kr = thread_getstatus(thread, flavor, (thread_state_t)state, &state_cnt); if (kr == KERN_SUCCESS) { if (code64) { kr = mach_exception_raise_state_identity( exc_port, retrieve_thread_self_fast(thread), retrieve_task_self_fast(thread->task), exception, code, codeCnt, &flavor, state, state_cnt, state, &state_cnt); } else { kr = exception_raise_state_identity(exc_port, retrieve_thread_self_fast(thread), retrieve_task_self_fast(thread->task), exception, small_code, codeCnt, &flavor, state, state_cnt, state, &state_cnt); } if (kr == MACH_MSG_SUCCESS) kr = thread_setstatus(thread, flavor, (thread_state_t)state, state_cnt); } return kr; } default: panic ("bad exception behavior!"); return KERN_FAILURE; }/* switch */ }