VM: Refactor mach_signal to Factor style
							parent
							
								
									0b5a12fdea
								
							
						
					
					
						commit
						302826c7be
					
				| 
						 | 
				
			
			@ -12,14 +12,14 @@ Modified for Factor by Slava Pestov */
 | 
			
		|||
 | 
			
		||||
#include "master.hpp"
 | 
			
		||||
 | 
			
		||||
namespace factor
 | 
			
		||||
{
 | 
			
		||||
namespace factor {
 | 
			
		||||
 | 
			
		||||
/* 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -28,35 +28,33 @@ 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. */
 | 
			
		||||
void factor_vm::call_fault_handler(
 | 
			
		||||
	exception_type_t exception,
 | 
			
		||||
void factor_vm::call_fault_handler(exception_type_t exception,
 | 
			
		||||
                                   exception_data_type_t code,
 | 
			
		||||
                                   MACH_EXC_STATE_TYPE* exc_state,
 | 
			
		||||
                                   MACH_THREAD_STATE_TYPE* thread_state,
 | 
			
		||||
	MACH_FLOAT_STATE_TYPE *float_state)
 | 
			
		||||
{
 | 
			
		||||
                                   MACH_FLOAT_STATE_TYPE* float_state) {
 | 
			
		||||
  cell handler = 0;
 | 
			
		||||
 | 
			
		||||
	if(exception == EXC_BAD_ACCESS)
 | 
			
		||||
	{
 | 
			
		||||
  if (exception == EXC_BAD_ACCESS) {
 | 
			
		||||
    signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state);
 | 
			
		||||
    signal_fault_pc = (cell) MACH_PROGRAM_COUNTER(thread_state);
 | 
			
		||||
    verify_memory_protection_error(signal_fault_addr);
 | 
			
		||||
    handler = (cell) factor::memory_signal_handler_impl;
 | 
			
		||||
	}
 | 
			
		||||
	else if(exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV)
 | 
			
		||||
	{
 | 
			
		||||
  } else if (exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV) {
 | 
			
		||||
    signal_fpu_status = fpu_status(mach_fpu_status(float_state));
 | 
			
		||||
    mach_clear_fpu_status(float_state);
 | 
			
		||||
    handler = (cell) factor::fp_signal_handler_impl;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		switch(exception)
 | 
			
		||||
		{
 | 
			
		||||
		case EXC_ARITHMETIC: signal_number = SIGFPE; break;
 | 
			
		||||
		case EXC_BAD_INSTRUCTION: signal_number = SIGILL; break;
 | 
			
		||||
		default: signal_number = SIGABRT; break;
 | 
			
		||||
  } else {
 | 
			
		||||
    switch (exception) {
 | 
			
		||||
      case EXC_ARITHMETIC:
 | 
			
		||||
        signal_number = SIGFPE;
 | 
			
		||||
        break;
 | 
			
		||||
      case EXC_BAD_INSTRUCTION:
 | 
			
		||||
        signal_number = SIGILL;
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        signal_number = SIGABRT;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handler = (cell) factor::synchronous_signal_handler_impl;
 | 
			
		||||
| 
						 | 
				
			
			@ -64,54 +62,45 @@ void factor_vm::call_fault_handler(
 | 
			
		|||
 | 
			
		||||
  FACTOR_ASSERT(handler != 0);
 | 
			
		||||
 | 
			
		||||
	dispatch_signal_handler(
 | 
			
		||||
		(cell*)&MACH_STACK_POINTER(thread_state),
 | 
			
		||||
  dispatch_signal_handler((cell*)&MACH_STACK_POINTER(thread_state),
 | 
			
		||||
                          (cell*)&MACH_PROGRAM_COUNTER(thread_state),
 | 
			
		||||
		(cell)handler
 | 
			
		||||
	);
 | 
			
		||||
                          (cell) handler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void call_fault_handler(
 | 
			
		||||
	mach_port_t thread,
 | 
			
		||||
	exception_type_t exception,
 | 
			
		||||
static void call_fault_handler(mach_port_t thread, exception_type_t exception,
 | 
			
		||||
                               exception_data_type_t code,
 | 
			
		||||
                               MACH_EXC_STATE_TYPE* exc_state,
 | 
			
		||||
                               MACH_THREAD_STATE_TYPE* thread_state,
 | 
			
		||||
	MACH_FLOAT_STATE_TYPE *float_state)
 | 
			
		||||
{
 | 
			
		||||
                               MACH_FLOAT_STATE_TYPE* float_state) {
 | 
			
		||||
  /* Look up the VM instance involved */
 | 
			
		||||
  THREADHANDLE thread_id = pthread_from_mach_thread_np(thread);
 | 
			
		||||
  FACTOR_ASSERT(thread_id);
 | 
			
		||||
	std::map<THREADHANDLE, factor_vm*>::const_iterator vm = thread_vms.find(thread_id);
 | 
			
		||||
  std::map<THREADHANDLE, factor_vm*>::const_iterator vm =
 | 
			
		||||
      thread_vms.find(thread_id);
 | 
			
		||||
 | 
			
		||||
  /* Handle the exception */
 | 
			
		||||
  if (vm != thread_vms.end())
 | 
			
		||||
		vm->second->call_fault_handler(exception,code,exc_state,thread_state,float_state);
 | 
			
		||||
    vm->second->call_fault_handler(exception, code, exc_state, thread_state,
 | 
			
		||||
                                   float_state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Handle an exception by invoking the user's fault handler and/or forwarding
 | 
			
		||||
   the duty to the previously installed handlers.	*/
 | 
			
		||||
extern "C"
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
extern "C" 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) {
 | 
			
		||||
  /* 10.6 likes to report exceptions from child processes too. Ignore those */
 | 
			
		||||
	if(task != mach_task_self()) return KERN_FAILURE;
 | 
			
		||||
  if (task != mach_task_self())
 | 
			
		||||
    return KERN_FAILURE;
 | 
			
		||||
 | 
			
		||||
  /* Get fault information and the faulting thread's register contents..
 | 
			
		||||
	
 | 
			
		||||
     See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html. */
 | 
			
		||||
  MACH_EXC_STATE_TYPE exc_state;
 | 
			
		||||
  mach_msg_type_number_t exc_state_count = MACH_EXC_STATE_COUNT;
 | 
			
		||||
	if (thread_get_state (thread, MACH_EXC_STATE_FLAVOR,
 | 
			
		||||
			      (natural_t *)&exc_state, &exc_state_count)
 | 
			
		||||
		!= KERN_SUCCESS)
 | 
			
		||||
	{
 | 
			
		||||
  if (thread_get_state(thread, MACH_EXC_STATE_FLAVOR, (natural_t*)&exc_state,
 | 
			
		||||
                       &exc_state_count) !=
 | 
			
		||||
      KERN_SUCCESS) {
 | 
			
		||||
    /* The thread is supposed to be suspended while the exception
 | 
			
		||||
       handler is called. This shouldn't fail. */
 | 
			
		||||
    return KERN_FAILURE;
 | 
			
		||||
| 
						 | 
				
			
			@ -120,9 +109,8 @@ catch_exception_raise (mach_port_t exception_port,
 | 
			
		|||
  MACH_THREAD_STATE_TYPE thread_state;
 | 
			
		||||
  mach_msg_type_number_t thread_state_count = MACH_THREAD_STATE_COUNT;
 | 
			
		||||
  if (thread_get_state(thread, MACH_THREAD_STATE_FLAVOR,
 | 
			
		||||
			      (natural_t *)&thread_state, &thread_state_count)
 | 
			
		||||
		!= KERN_SUCCESS)
 | 
			
		||||
	{
 | 
			
		||||
                       (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;
 | 
			
		||||
| 
						 | 
				
			
			@ -131,9 +119,8 @@ catch_exception_raise (mach_port_t exception_port,
 | 
			
		|||
  MACH_FLOAT_STATE_TYPE float_state;
 | 
			
		||||
  mach_msg_type_number_t float_state_count = MACH_FLOAT_STATE_COUNT;
 | 
			
		||||
  if (thread_get_state(thread, MACH_FLOAT_STATE_FLAVOR,
 | 
			
		||||
			      (natural_t *)&float_state, &float_state_count)
 | 
			
		||||
		!= KERN_SUCCESS)
 | 
			
		||||
	{
 | 
			
		||||
                       (natural_t*)&float_state, &float_state_count) !=
 | 
			
		||||
      KERN_SUCCESS) {
 | 
			
		||||
    /* The thread is supposed to be suspended while the exception
 | 
			
		||||
       handler is called. This shouldn't fail. */
 | 
			
		||||
    return KERN_FAILURE;
 | 
			
		||||
| 
						 | 
				
			
			@ -141,22 +128,20 @@ catch_exception_raise (mach_port_t exception_port,
 | 
			
		|||
 | 
			
		||||
  /* Modify registers so to have the thread resume executing the
 | 
			
		||||
     fault handler */
 | 
			
		||||
	call_fault_handler(thread,exception,code[0],&exc_state,&thread_state,&float_state);
 | 
			
		||||
  call_fault_handler(thread, exception, code[0], &exc_state, &thread_state,
 | 
			
		||||
                     &float_state);
 | 
			
		||||
 | 
			
		||||
  /* Set the faulting thread's register contents..
 | 
			
		||||
	
 | 
			
		||||
     See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html. */
 | 
			
		||||
  if (thread_set_state(thread, MACH_FLOAT_STATE_FLAVOR,
 | 
			
		||||
			      (natural_t *)&float_state, float_state_count)
 | 
			
		||||
		!= KERN_SUCCESS)
 | 
			
		||||
	{
 | 
			
		||||
                       (natural_t*)&float_state, float_state_count) !=
 | 
			
		||||
      KERN_SUCCESS) {
 | 
			
		||||
    return KERN_FAILURE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (thread_set_state(thread, MACH_THREAD_STATE_FLAVOR,
 | 
			
		||||
			      (natural_t *)&thread_state, thread_state_count)
 | 
			
		||||
		!= KERN_SUCCESS)
 | 
			
		||||
	{
 | 
			
		||||
                       (natural_t*)&thread_state, thread_state_count) !=
 | 
			
		||||
      KERN_SUCCESS) {
 | 
			
		||||
    return KERN_FAILURE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -164,38 +149,30 @@ catch_exception_raise (mach_port_t exception_port,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/* The main function of the thread listening for exceptions.  */
 | 
			
		||||
static void *
 | 
			
		||||
mach_exception_thread (void *arg)
 | 
			
		||||
{
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
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
 | 
			
		||||
		{
 | 
			
		||||
    struct {
 | 
			
		||||
      mach_msg_header_t head;
 | 
			
		||||
      mach_msg_body_t msgh_body;
 | 
			
		||||
      char data[1024];
 | 
			
		||||
		}
 | 
			
		||||
		msg;
 | 
			
		||||
    } msg;
 | 
			
		||||
    /* Buffer for a reply message. */
 | 
			
		||||
		struct
 | 
			
		||||
		{
 | 
			
		||||
    struct {
 | 
			
		||||
      mach_msg_header_t head;
 | 
			
		||||
      char data[1024];
 | 
			
		||||
		}
 | 
			
		||||
		reply;
 | 
			
		||||
    } 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)
 | 
			
		||||
		{
 | 
			
		||||
    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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -204,10 +181,9 @@ mach_exception_thread (void *arg)
 | 
			
		|||
    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)
 | 
			
		||||
		{
 | 
			
		||||
    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();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -215,22 +191,23 @@ mach_exception_thread (void *arg)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize the Mach exception handler thread. */
 | 
			
		||||
void mach_initialize ()
 | 
			
		||||
{
 | 
			
		||||
void mach_initialize() {
 | 
			
		||||
  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)
 | 
			
		||||
  if (mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, &our_exception_port) !=
 | 
			
		||||
      KERN_SUCCESS)
 | 
			
		||||
    fatal_error("mach_port_allocate() failed", 0);
 | 
			
		||||
 | 
			
		||||
	/* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html.  */
 | 
			
		||||
  /* 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)
 | 
			
		||||
                             MACH_MSG_TYPE_MAKE_SEND) !=
 | 
			
		||||
      KERN_SUCCESS)
 | 
			
		||||
    fatal_error("mach_port_insert_right() failed", 0);
 | 
			
		||||
 | 
			
		||||
  /* The exceptions we want to catch. */
 | 
			
		||||
| 
						 | 
				
			
			@ -243,11 +220,11 @@ void mach_initialize ()
 | 
			
		|||
     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.
 | 
			
		||||
	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html.	*/
 | 
			
		||||
     been asked, and we don't need to bother about it. See
 | 
			
		||||
     http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html.	*/
 | 
			
		||||
  if (task_set_exception_ports(self, mask, our_exception_port,
 | 
			
		||||
		EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
 | 
			
		||||
		!= KERN_SUCCESS)
 | 
			
		||||
                               EXCEPTION_DEFAULT, MACHINE_THREAD_STATE) !=
 | 
			
		||||
      KERN_SUCCESS)
 | 
			
		||||
    fatal_error("task_set_exception_ports() failed", 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,48 +36,28 @@ Modified for Factor by Slava Pestov */
 | 
			
		|||
   allowing the thread to continue from the point of the exception; otherwise,
 | 
			
		||||
   no reply message is sent and the called routine must have dealt with the
 | 
			
		||||
   exception thread directly.  */
 | 
			
		||||
extern "C" boolean_t exc_server (mach_msg_header_t *request_msg, mach_msg_header_t *reply_msg);
 | 
			
		||||
extern "C" boolean_t exc_server(mach_msg_header_t* request_msg,
 | 
			
		||||
                                mach_msg_header_t* reply_msg);
 | 
			
		||||
 | 
			
		||||
/* http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/catch_exception_raise.html
 | 
			
		||||
   These functions are defined in this file, and called by exc_server.
 | 
			
		||||
   FIXME: What needs to be done when this code is put into a shared library? */
 | 
			
		||||
extern "C"
 | 
			
		||||
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,
 | 
			
		||||
extern "C" 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);
 | 
			
		||||
extern "C"
 | 
			
		||||
kern_return_t
 | 
			
		||||
catch_exception_raise_state (mach_port_t exception_port,
 | 
			
		||||
			     exception_type_t exception,
 | 
			
		||||
			     exception_data_t code,
 | 
			
		||||
			     mach_msg_type_number_t code_count,
 | 
			
		||||
			     thread_state_flavor_t *flavor,
 | 
			
		||||
			     thread_state_t in_state,
 | 
			
		||||
			     mach_msg_type_number_t in_state_count,
 | 
			
		||||
			     thread_state_t out_state,
 | 
			
		||||
extern "C" kern_return_t catch_exception_raise_state(
 | 
			
		||||
    mach_port_t exception_port, exception_type_t exception,
 | 
			
		||||
    exception_data_t code, mach_msg_type_number_t code_count,
 | 
			
		||||
    thread_state_flavor_t* flavor, thread_state_t in_state,
 | 
			
		||||
    mach_msg_type_number_t in_state_count, thread_state_t out_state,
 | 
			
		||||
    mach_msg_type_number_t* out_state_count);
 | 
			
		||||
 | 
			
		||||
extern "C"
 | 
			
		||||
kern_return_t
 | 
			
		||||
catch_exception_raise_state_identity (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 codeCnt,
 | 
			
		||||
				      thread_state_flavor_t *flavor,
 | 
			
		||||
				      thread_state_t in_state,
 | 
			
		||||
				      mach_msg_type_number_t in_state_count,
 | 
			
		||||
				      thread_state_t out_state,
 | 
			
		||||
				      mach_msg_type_number_t *out_state_count);
 | 
			
		||||
extern "C" kern_return_t catch_exception_raise_state_identity(
 | 
			
		||||
    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 codeCnt, thread_state_flavor_t* flavor,
 | 
			
		||||
    thread_state_t in_state, mach_msg_type_number_t in_state_count,
 | 
			
		||||
    thread_state_t out_state, mach_msg_type_number_t* out_state_count);
 | 
			
		||||
 | 
			
		||||
namespace factor
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
void mach_initialize ();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
namespace factor { void mach_initialize(); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue