From 302826c7be2de94bd4570919b399166e87cb3e02 Mon Sep 17 00:00:00 2001 From: Erik Charlebois Date: Sat, 11 May 2013 22:11:09 -0400 Subject: [PATCH] VM: Refactor mach_signal to Factor style --- vm/mach_signal.cpp | 415 +++++++++++++++++++++------------------------ vm/mach_signal.hpp | 62 +++---- 2 files changed, 217 insertions(+), 260 deletions(-) diff --git a/vm/mach_signal.cpp b/vm/mach_signal.cpp index a4fe269b3d..abbf7ae8c6 100644 --- a/vm/mach_signal.cpp +++ b/vm/mach_signal.cpp @@ -12,243 +12,220 @@ 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. -http://www.wodeveloper.com/omniLists/macosx-dev/2000/June/msg00137.html */ + 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. */ -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) -{ - cell handler = 0; - - 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) - { - 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; - } - - handler = (cell)factor::synchronous_signal_handler_impl; - } - - FACTOR_ASSERT(handler != 0); - - dispatch_signal_handler( - (cell*)&MACH_STACK_POINTER(thread_state), - (cell*)&MACH_PROGRAM_COUNTER(thread_state), - (cell)handler - ); + 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, + exception_data_type_t code, + MACH_EXC_STATE_TYPE* exc_state, + MACH_THREAD_STATE_TYPE* thread_state, + MACH_FLOAT_STATE_TYPE* float_state) { + cell handler = 0; + + 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) { + 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; + } + + handler = (cell) factor::synchronous_signal_handler_impl; + } + + FACTOR_ASSERT(handler != 0); + + dispatch_signal_handler((cell*)&MACH_STACK_POINTER(thread_state), + (cell*)&MACH_PROGRAM_COUNTER(thread_state), + (cell) handler); } -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) -{ - /* Look up the VM instance involved */ - THREADHANDLE thread_id = pthread_from_mach_thread_np(thread); - FACTOR_ASSERT(thread_id); - std::map::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); +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) { + /* Look up the VM instance involved */ + THREADHANDLE thread_id = pthread_from_mach_thread_np(thread); + FACTOR_ASSERT(thread_id); + std::map::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); } /* 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) -{ - /* 10.6 likes to report exceptions from child processes too. Ignore those */ - 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) - { - /* The thread is supposed to be suspended while the exception - handler is called. This shouldn't fail. */ - return KERN_FAILURE; - } - - 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) - { - /* The thread is supposed to be suspended while the exception - handler is called. This shouldn't fail. */ - return KERN_FAILURE; - } - - 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) - { - /* 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 */ - 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) - { - return KERN_FAILURE; - } - - if (thread_set_state (thread, MACH_THREAD_STATE_FLAVOR, - (natural_t *)&thread_state, thread_state_count) - != KERN_SUCCESS) - { - return KERN_FAILURE; - } - - return KERN_SUCCESS; + 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) { + /* 10.6 likes to report exceptions from child processes too. Ignore those */ + 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) { + /* The thread is supposed to be suspended while the exception + handler is called. This shouldn't fail. */ + return KERN_FAILURE; + } + + 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) { + /* The thread is supposed to be suspended while the exception + handler is called. This shouldn't fail. */ + return KERN_FAILURE; + } + + 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) { + /* 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 */ + 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) { + return KERN_FAILURE; + } + + if (thread_set_state(thread, MACH_THREAD_STATE_FLAVOR, + (natural_t*)&thread_state, thread_state_count) != + 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; - /* Buffer for a reply message. */ - 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 (); - } - } - return NULL; // quiet warning +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; + /* Buffer for a reply message. */ + 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(); + } + } + return NULL; // quiet warning } /* Initialize the Mach exception handler thread. */ -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) - fatal_error("mach_port_allocate() failed",0); - - /* 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) - fatal_error("mach_port_insert_right() failed",0); - - /* The exceptions we want to catch. */ - mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC; - - /* Create the thread listening on the exception port. */ - start_thread(mach_exception_thread,NULL); - - /* 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. - 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) - fatal_error("task_set_exception_ports() failed",0); +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) + fatal_error("mach_port_allocate() failed", 0); + + /* 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) + fatal_error("mach_port_insert_right() failed", 0); + + /* The exceptions we want to catch. */ + mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC; + + /* Create the thread listening on the exception port. */ + start_thread(mach_exception_thread, NULL); + + /* 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. 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) + fatal_error("task_set_exception_ports() failed", 0); } } diff --git a/vm/mach_signal.hpp b/vm/mach_signal.hpp index a1961359d1..acf9f69788 100644 --- a/vm/mach_signal.hpp +++ b/vm/mach_signal.hpp @@ -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, - 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, - 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 (); - -} +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, + 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(); } -- 2.34.1