VM: Refactor mach_signal to Factor style
parent
0b5a12fdea
commit
302826c7be
|
@ -12,106 +12,95 @@ 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,
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (exception == EXC_BAD_ACCESS) {
|
||||
signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state);
|
||||
signal_fault_pc = (cell)MACH_PROGRAM_COUNTER(thread_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)
|
||||
{
|
||||
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::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;
|
||||
handler = (cell) factor::synchronous_signal_handler_impl;
|
||||
}
|
||||
|
||||
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_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<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)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
|
@ -119,10 +108,9 @@ 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)
|
||||
{
|
||||
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;
|
||||
|
@ -130,10 +118,9 @@ 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)
|
||||
{
|
||||
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;
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (thread_set_state(thread, MACH_THREAD_STATE_FLAVOR,
|
||||
(natural_t*)&thread_state, thread_state_count) !=
|
||||
KERN_SUCCESS) {
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -164,91 +149,83 @@ 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)
|
||||
{
|
||||
abort ();
|
||||
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);
|
||||
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 ();
|
||||
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 ()
|
||||
{
|
||||
void mach_initialize() {
|
||||
mach_port_t self;
|
||||
exception_mask_t mask;
|
||||
|
||||
self = mach_task_self ();
|
||||
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);
|
||||
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);
|
||||
/* 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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
mach_msg_type_number_t *out_state_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);
|
||||
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