| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | /* Fault handler information.  MacOSX version.
 | 
					
						
							|  |  |  | Copyright (C) 1993-1999, 2002-2003  Bruno Haible <clisp.org at bruno> | 
					
						
							| 
									
										
										
										
											2009-05-04 02:46:13 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | Copyright (C) 2003  Paolo Bonzini <gnu.org at bonzini> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Used under BSD license with permission from Paolo Bonzini and Bruno Haible, | 
					
						
							|  |  |  | 2005-03-10: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | http://sourceforge.net/mailarchive/message.php?msg_name=200503102200.32002.bruno%40clisp.org
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Modified for Factor by Slava Pestov */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "master.hpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-04 02:46:13 -04:00
										 |  |  | namespace factor | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | /* The exception port on which our thread listens. */ | 
					
						
							|  |  |  | mach_port_t our_exception_port; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* The following sources were used as a *reference* for this exception handling
 | 
					
						
							|  |  |  | code: | 
					
						
							|  |  |  | 1. Apple's mach/xnu documentation | 
					
						
							|  |  |  | 2. Timothy J. Wood's "Mach Exception Handlers 101" post to the | 
					
						
							|  |  |  | omnigroup's macosx-dev list. | 
					
						
							|  |  |  | http://www.wodeveloper.com/omniLists/macosx-dev/2000/June/msg00137.html */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Modify a suspended thread's thread_state so that when the thread resumes
 | 
					
						
							|  |  |  | executing, the call frame of the current C primitive (if any) is rewound, and | 
					
						
							|  |  |  | the appropriate Factor error is thrown from the top-most Factor frame. */ | 
					
						
							| 
									
										
										
										
											2009-09-23 14:05:46 -04:00
										 |  |  | void factor_vm::call_fault_handler( | 
					
						
							| 
									
										
										
										
											2009-10-02 10:37:04 -04:00
										 |  |  | 	exception_type_t exception, | 
					
						
							|  |  |  | 	exception_data_type_t code, | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 	MACH_EXC_STATE_TYPE *exc_state, | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 	MACH_THREAD_STATE_TYPE *thread_state, | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 	MACH_FLOAT_STATE_TYPE *float_state) | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* There is a race condition here, but in practice an exception
 | 
					
						
							|  |  |  | 	delivered during stack frame setup/teardown or while transitioning | 
					
						
							|  |  |  | 	from Factor to C is a sign of things seriously gone wrong, not just | 
					
						
							|  |  |  | 	a divide by zero or stack underflow in the listener */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Are we in compiled Factor code? Then use the current stack pointer */ | 
					
						
							| 
									
										
										
										
											2009-09-14 03:00:28 -04:00
										 |  |  | 	if(in_code_heap_p(MACH_PROGRAM_COUNTER(thread_state))) | 
					
						
							|  |  |  | 		signal_callstack_top = (stack_frame *)MACH_STACK_POINTER(thread_state); | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 	/* Are we in C? Then use the saved callstack top */ | 
					
						
							|  |  |  | 	else | 
					
						
							| 
									
										
										
										
											2009-09-14 03:00:28 -04:00
										 |  |  | 		signal_callstack_top = NULL; | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-22 06:22:59 -04:00
										 |  |  | 	MACH_STACK_POINTER(thread_state) = align_stack_pointer(MACH_STACK_POINTER(thread_state)); | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Now we point the program counter at the right handler function. */ | 
					
						
							|  |  |  | 	if(exception == EXC_BAD_ACCESS) | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2009-09-14 03:00:28 -04:00
										 |  |  | 		signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state); | 
					
						
							|  |  |  | 		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::memory_signal_handler_impl; | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-09-06 09:44:25 -04:00
										 |  |  | 	else if(exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV) | 
					
						
							| 
									
										
										
										
											2009-09-12 19:15:16 -04:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2009-09-14 03:00:28 -04:00
										 |  |  | 		signal_fpu_status = fpu_status(mach_fpu_status(float_state)); | 
					
						
							|  |  |  | 		mach_clear_fpu_status(float_state); | 
					
						
							|  |  |  | 		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::fp_signal_handler_impl; | 
					
						
							| 
									
										
										
										
											2009-09-12 19:15:16 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2009-10-22 06:22:59 -04:00
										 |  |  | 		switch(exception) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 		case EXC_ARITHMETIC: signal_number = SIGFPE; break; | 
					
						
							|  |  |  | 		case EXC_BAD_INSTRUCTION: signal_number = SIGILL; break; | 
					
						
							|  |  |  | 		default: signal_number = SIGABRT; break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-14 03:00:28 -04:00
										 |  |  | 		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::misc_signal_handler_impl; | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | static void call_fault_handler( | 
					
						
							|  |  |  | 	mach_port_t thread, | 
					
						
							|  |  |  | 	exception_type_t exception, | 
					
						
							| 
									
										
										
										
											2009-10-02 10:37:04 -04:00
										 |  |  | 	exception_data_type_t code, | 
					
						
							|  |  |  | 	MACH_EXC_STATE_TYPE *exc_state, | 
					
						
							|  |  |  | 	MACH_THREAD_STATE_TYPE *thread_state, | 
					
						
							|  |  |  | 	MACH_FLOAT_STATE_TYPE *float_state) | 
					
						
							| 
									
										
										
										
											2009-09-14 03:00:28 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 	THREADHANDLE thread_id = pthread_from_mach_thread_np(thread); | 
					
						
							|  |  |  | 	assert(thread_id); | 
					
						
							| 
									
										
										
										
											2009-10-19 03:21:11 -04:00
										 |  |  | 	std::map<THREADHANDLE, factor_vm*>::const_iterator vm = thread_vms.find(thread_id); | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 	if (vm != thread_vms.end()) | 
					
						
							|  |  |  | 	    vm->second->call_fault_handler(exception,code,exc_state,thread_state,float_state); | 
					
						
							| 
									
										
										
										
											2009-09-14 03:00:28 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | /* Handle an exception by invoking the user's fault handler and/or forwarding
 | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | the duty to the previously installed handlers.	*/ | 
					
						
							| 
									
										
										
										
											2009-05-02 21:01:54 -04:00
										 |  |  | extern "C" | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | kern_return_t | 
					
						
							|  |  |  | catch_exception_raise (mach_port_t exception_port, | 
					
						
							|  |  |  | 	mach_port_t thread, | 
					
						
							|  |  |  | 	mach_port_t task, | 
					
						
							|  |  |  | 	exception_type_t exception, | 
					
						
							|  |  |  | 	exception_data_t code, | 
					
						
							|  |  |  | 	mach_msg_type_number_t code_count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	MACH_EXC_STATE_TYPE exc_state; | 
					
						
							|  |  |  | 	MACH_THREAD_STATE_TYPE thread_state; | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 	MACH_FLOAT_STATE_TYPE float_state; | 
					
						
							|  |  |  | 	mach_msg_type_number_t exc_state_count, thread_state_count, float_state_count; | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Get fault information and the faulting thread's register contents..
 | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html.	*/
 | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 	exc_state_count = MACH_EXC_STATE_COUNT; | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 	if (thread_get_state (thread, MACH_EXC_STATE_FLAVOR, | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 			      (natural_t *)&exc_state, &exc_state_count) | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 		!= KERN_SUCCESS) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		/* The thread is supposed to be suspended while the exception
 | 
					
						
							|  |  |  | 		handler is called. This shouldn't fail. */ | 
					
						
							|  |  |  | 		return KERN_FAILURE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 	thread_state_count = MACH_THREAD_STATE_COUNT; | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 	if (thread_get_state (thread, MACH_THREAD_STATE_FLAVOR, | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 			      (natural_t *)&thread_state, &thread_state_count) | 
					
						
							|  |  |  | 		!= KERN_SUCCESS) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		/* The thread is supposed to be suspended while the exception
 | 
					
						
							|  |  |  | 		handler is called. This shouldn't fail. */ | 
					
						
							|  |  |  | 		return KERN_FAILURE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 	float_state_count = MACH_FLOAT_STATE_COUNT; | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 	if (thread_get_state (thread, MACH_FLOAT_STATE_FLAVOR, | 
					
						
							|  |  |  | 			      (natural_t *)&float_state, &float_state_count) | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 		!= KERN_SUCCESS) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		/* The thread is supposed to be suspended while the exception
 | 
					
						
							|  |  |  | 		handler is called. This shouldn't fail. */ | 
					
						
							|  |  |  | 		return KERN_FAILURE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Modify registers so to have the thread resume executing the
 | 
					
						
							|  |  |  | 	fault handler */ | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 	call_fault_handler(thread,exception,code[0],&exc_state,&thread_state,&float_state); | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Set the faulting thread's register contents..
 | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html.	*/
 | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 	if (thread_set_state (thread, MACH_FLOAT_STATE_FLAVOR, | 
					
						
							|  |  |  | 			      (natural_t *)&float_state, float_state_count) | 
					
						
							|  |  |  | 		!= KERN_SUCCESS) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		return KERN_FAILURE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 	if (thread_set_state (thread, MACH_THREAD_STATE_FLAVOR, | 
					
						
							| 
									
										
										
										
											2009-09-14 00:37:28 -04:00
										 |  |  | 			      (natural_t *)&thread_state, thread_state_count) | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 		!= KERN_SUCCESS) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		return KERN_FAILURE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return KERN_SUCCESS; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* The main function of the thread listening for exceptions.  */ | 
					
						
							|  |  |  | static void * | 
					
						
							|  |  |  | mach_exception_thread (void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	for (;;) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		/* These two structures contain some private kernel data. We don't need
 | 
					
						
							|  |  |  | 		to access any of it so we don't bother defining a proper struct. The | 
					
						
							|  |  |  | 		correct definitions are in the xnu source code. */ | 
					
						
							|  |  |  | 		/* Buffer for a message to be received.  */ | 
					
						
							|  |  |  | 		struct | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			mach_msg_header_t head; | 
					
						
							|  |  |  | 			mach_msg_body_t msgh_body; | 
					
						
							|  |  |  | 			char data[1024]; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		msg; | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 		/* Buffer for a reply message.	*/ | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 		struct | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			mach_msg_header_t head; | 
					
						
							|  |  |  | 			char data[1024]; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		reply; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		mach_msg_return_t retval; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Wait for a message on the exception port.  */ | 
					
						
							|  |  |  | 		retval = mach_msg (&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, | 
					
						
							|  |  |  | 			sizeof (msg), our_exception_port, | 
					
						
							|  |  |  | 			MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); | 
					
						
							|  |  |  | 		if (retval != MACH_MSG_SUCCESS) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			abort (); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Handle the message: Call exc_server, which will call
 | 
					
						
							|  |  |  | 		catch_exception_raise and produce a reply message.  */ | 
					
						
							|  |  |  | 		exc_server (&msg.head, &reply.head); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Send the reply.  */ | 
					
						
							|  |  |  | 		if (mach_msg (&reply.head, MACH_SEND_MSG, reply.head.msgh_size, | 
					
						
							|  |  |  | 			0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL) | 
					
						
							|  |  |  | 			!= MACH_MSG_SUCCESS) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			abort (); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Initialize the Mach exception handler thread. */ | 
					
						
							| 
									
										
										
										
											2009-05-05 12:33:35 -04:00
										 |  |  | void mach_initialize () | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	mach_port_t self; | 
					
						
							|  |  |  | 	exception_mask_t mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	self = mach_task_self (); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Allocate a port on which the thread shall listen for exceptions.  */ | 
					
						
							|  |  |  | 	if (mach_port_allocate (self, MACH_PORT_RIGHT_RECEIVE, &our_exception_port) | 
					
						
							|  |  |  | 		!= KERN_SUCCESS) | 
					
						
							| 
									
										
										
										
											2009-08-25 03:20:58 -04:00
										 |  |  | 		fatal_error("mach_port_allocate() failed",0); | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html.  */ | 
					
						
							|  |  |  | 	if (mach_port_insert_right (self, our_exception_port, our_exception_port, | 
					
						
							|  |  |  | 		MACH_MSG_TYPE_MAKE_SEND) | 
					
						
							|  |  |  | 		!= KERN_SUCCESS) | 
					
						
							| 
									
										
										
										
											2009-08-25 03:20:58 -04:00
										 |  |  | 		fatal_error("mach_port_insert_right() failed",0); | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* The exceptions we want to catch. */ | 
					
						
							| 
									
										
										
										
											2009-10-22 06:22:59 -04:00
										 |  |  | 	mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC; | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Create the thread listening on the exception port.  */ | 
					
						
							| 
									
										
										
										
											2009-08-25 01:35:54 -04:00
										 |  |  | 	start_thread(mach_exception_thread,NULL); | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Replace the exception port info for these exceptions with our own.
 | 
					
						
							|  |  |  | 	Note that we replace the exception port for the entire task, not only | 
					
						
							|  |  |  | 	for a particular thread.  This has the effect that when our exception | 
					
						
							|  |  |  | 	port gets the message, the thread specific exception port has already | 
					
						
							|  |  |  | 	been asked, and we don't need to bother about it. | 
					
						
							| 
									
										
										
										
											2009-10-02 13:00:01 -04:00
										 |  |  | 	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html.	*/
 | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | 	if (task_set_exception_ports (self, mask, our_exception_port, | 
					
						
							|  |  |  | 		EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) | 
					
						
							|  |  |  | 		!= KERN_SUCCESS) | 
					
						
							| 
									
										
										
										
											2009-08-25 03:20:58 -04:00
										 |  |  | 		fatal_error("task_set_exception_ports() failed",0); | 
					
						
							| 
									
										
										
										
											2009-05-02 05:04:19 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-05-04 02:46:13 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | } |