implement mach exception handling
parent
53b52d5769
commit
b15a262e54
3
Makefile
3
Makefile
|
@ -16,7 +16,8 @@ UNIX_OBJS = native/unix/file.o \
|
|||
native/unix/signal.o \
|
||||
native/unix/ffi.o \
|
||||
native/unix/run.o \
|
||||
native/unix/memory.o
|
||||
native/unix/memory.o \
|
||||
native/unix/mach_signal.o
|
||||
|
||||
WIN32_OBJS = native/win32/ffi.o \
|
||||
native/win32/file.o \
|
||||
|
|
|
@ -38,13 +38,6 @@
|
|||
it anyway?
|
||||
- apropos: use new smarter completion?
|
||||
- signal handler should not lose stack pointers
|
||||
- sigsegv handling on OS X:
|
||||
|
||||
http://developer.apple.com/technotes/tn2004/tn2123.html#SECLIMITATIONS
|
||||
http://www.caddr.com/macho/archives/sbcl-devel/2005-3/4742.html
|
||||
http://www.caddr.com/macho/archives/sbcl-devel/2005-3/4764.html
|
||||
http://clozure.com/cgi-bin/viewcvs.cgi/ccl/lisp-kernel/lisp-exceptions.c?rev=1.9&content-type=text/vnd.viewcvs-markup
|
||||
|
||||
- http keep alive, and range get
|
||||
|
||||
+ ffi:
|
||||
|
|
|
@ -23,6 +23,8 @@ C: world ( -- world )
|
|||
world get world-content @center frame-add ;
|
||||
|
||||
: set-status ( gadget -- )
|
||||
#! Set the status bar gadget to the given gadget. It must
|
||||
#! implement the set-message generic word.
|
||||
world get 2dup set-world-status
|
||||
world-content @bottom frame-add ;
|
||||
|
||||
|
@ -44,11 +46,11 @@ C: world ( -- world )
|
|||
<gadget> dup add-layer dup world get set-world-glass
|
||||
dupd add-gadget prefer ;
|
||||
|
||||
: world-clip ( -- rect )
|
||||
@{ 0 0 0 }@ width get height get 0 3array <rect> ;
|
||||
: world-clip ( -- )
|
||||
@{ 0 0 0 }@ width get height get 0 3array <rect> clip set ;
|
||||
|
||||
: draw-world ( -- )
|
||||
world get [ world-clip clip set draw-gadget ] with-gl-surface ;
|
||||
[ world-clip world get draw-gadget ] with-gl-surface ;
|
||||
|
||||
! Status bar protocol
|
||||
GENERIC: set-message ( string/f status -- )
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
/* Fault handler information. MacOSX version.
|
||||
Copyright (C) 1993-1999, 2002-2003 Bruno Haible <clisp.org at bruno>
|
||||
Copyright (C) 2003 Paolo Bonzini <gnu.org at bonzini>
|
||||
|
||||
Used under BSD license with permission from Paolo Bonzini and Bruno Haible,
|
||||
2005-03-10 */
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include "mach_signal.h"
|
||||
|
||||
/* 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.
|
||||
www.omnigroup.com/mailman/archive/macosx-dev/2000-June/002030.html */
|
||||
|
||||
/* The exception port on which our thread listens. */
|
||||
static mach_port_t our_exception_port;
|
||||
|
||||
/* Communication area for the exception state and thread state. */
|
||||
static SIGSEGV_THREAD_STATE_TYPE save_thread_state;
|
||||
|
||||
/* A handler that is called in the faulting thread. It terminates the thread. */
|
||||
static void
|
||||
terminating_handler ()
|
||||
{
|
||||
/* Dump core. */
|
||||
raise (SIGSEGV);
|
||||
|
||||
/* Seriously. */
|
||||
abort ();
|
||||
}
|
||||
|
||||
|
||||
/* Handle an exception by invoking the user's fault handler and/or forwarding
|
||||
the duty to the previously installed handlers. */
|
||||
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)
|
||||
{
|
||||
#ifdef SIGSEGV_EXC_STATE_TYPE
|
||||
SIGSEGV_EXC_STATE_TYPE exc_state;
|
||||
#endif
|
||||
SIGSEGV_THREAD_STATE_TYPE thread_state;
|
||||
mach_msg_type_number_t state_count;
|
||||
unsigned long addr;
|
||||
unsigned long sp;
|
||||
|
||||
/* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html. */
|
||||
#ifdef SIGSEGV_EXC_STATE_TYPE
|
||||
state_count = SIGSEGV_EXC_STATE_COUNT;
|
||||
if (thread_get_state (thread, SIGSEGV_EXC_STATE_FLAVOR,
|
||||
(void *) &exc_state, &state_count)
|
||||
!= KERN_SUCCESS)
|
||||
{
|
||||
/* The thread is supposed to be suspended while the exception handler
|
||||
is called. This shouldn't fail. */
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
state_count = SIGSEGV_THREAD_STATE_COUNT;
|
||||
if (thread_get_state (thread, SIGSEGV_THREAD_STATE_FLAVOR,
|
||||
(void *) &thread_state, &state_count)
|
||||
!= KERN_SUCCESS)
|
||||
{
|
||||
/* The thread is supposed to be suspended while the exception handler
|
||||
is called. This shouldn't fail. */
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
addr = (unsigned long) (SIGSEGV_FAULT_ADDRESS (thread_state, exc_state));
|
||||
sp = (unsigned long) (SIGSEGV_STACK_POINTER (thread_state));
|
||||
|
||||
/* Got the thread's state. Now extract the address that caused the
|
||||
fault and invoke the user's handler. */
|
||||
save_thread_state = thread_state;
|
||||
|
||||
/* If the fault address is near the stack pointer, it's a stack overflow.
|
||||
Otherwise, treat it like a normal SIGSEGV. */
|
||||
SIGSEGV_PROGRAM_COUNTER (thread_state) = (unsigned long) terminating_handler;
|
||||
|
||||
/* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html. */
|
||||
if (thread_set_state (thread, SIGSEGV_THREAD_STATE_FLAVOR,
|
||||
(void *) &thread_state, 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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the Mach exception handler thread.
|
||||
Return 0 if OK, -1 on error. */
|
||||
int mach_initialize ()
|
||||
{
|
||||
mach_port_t self;
|
||||
exception_mask_t mask;
|
||||
pthread_attr_t attr;
|
||||
pthread_t thread;
|
||||
|
||||
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)
|
||||
return -1;
|
||||
|
||||
/* 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)
|
||||
return -1;
|
||||
|
||||
/* The exceptions we want to catch. Only EXC_BAD_ACCESS is interesting
|
||||
for us (see above in function catch_exception_raise). */
|
||||
mask = EXC_MASK_BAD_ACCESS;
|
||||
|
||||
/* Create the thread listening on the exception port. */
|
||||
if (pthread_attr_init (&attr) != 0)
|
||||
return -1;
|
||||
if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED) != 0)
|
||||
return -1;
|
||||
if (pthread_create (&thread, &attr, mach_exception_thread, NULL) != 0)
|
||||
return -1;
|
||||
pthread_attr_destroy (&attr);
|
||||
|
||||
/* 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)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,86 @@
|
|||
#ifdef __APPLE__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <mach/thread_status.h>
|
||||
#include <mach/exception.h>
|
||||
#include <mach/task.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/* For MacOSX. */
|
||||
#ifndef SS_DISABLE
|
||||
#define SS_DISABLE SA_DISABLE
|
||||
#endif
|
||||
|
||||
/* This is not defined in any header, although documented. */
|
||||
|
||||
/* http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/exc_server.html says:
|
||||
The exc_server function is the MIG generated server handling function
|
||||
to handle messages from the kernel relating to the occurrence of an
|
||||
exception in a thread. Such messages are delivered to the exception port
|
||||
set via thread_set_exception_ports or task_set_exception_ports. When an
|
||||
exception occurs in a thread, the thread sends an exception message to its
|
||||
exception port, blocking in the kernel waiting for the receipt of a reply.
|
||||
The exc_server function performs all necessary argument handling for this
|
||||
kernel message and calls catch_exception_raise, catch_exception_raise_state
|
||||
or catch_exception_raise_state_identity, which should handle the exception.
|
||||
If the called routine returns KERN_SUCCESS, a reply message will be sent,
|
||||
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 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? */
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
||||
#define SIGSEGV_EXC_STATE_TYPE ppc_exception_state_t
|
||||
#define SIGSEGV_EXC_STATE_FLAVOR PPC_EXCEPTION_STATE
|
||||
#define SIGSEGV_EXC_STATE_COUNT PPC_EXCEPTION_STATE_COUNT
|
||||
#define SIGSEGV_THREAD_STATE_TYPE ppc_thread_state_t
|
||||
#define SIGSEGV_THREAD_STATE_FLAVOR PPC_THREAD_STATE
|
||||
#define SIGSEGV_THREAD_STATE_COUNT PPC_THREAD_STATE_COUNT
|
||||
#define SIGSEGV_FAULT_ADDRESS(thr_state,exc_state) (exc_state).dar
|
||||
#define SIGSEGV_STACK_POINTER(thr_state) (thr_state).r1
|
||||
#define SIGSEGV_PROGRAM_COUNTER(thr_state) (thr_state).srr0
|
||||
|
||||
int mach_initialize ();
|
||||
|
||||
#endif
|
|
@ -1,4 +1,5 @@
|
|||
#include "../factor.h"
|
||||
#include "mach_signal.h"
|
||||
|
||||
void signal_handler(int signal, siginfo_t* siginfo, void* uap)
|
||||
{
|
||||
|
@ -16,6 +17,13 @@ void dump_stack_signal(int signal, siginfo_t* siginfo, void* uap)
|
|||
interrupt = true;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int sigsegv_handler(void *fault_address, int serious)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void init_signals(void)
|
||||
{
|
||||
struct sigaction custom_sigaction;
|
||||
|
@ -32,9 +40,12 @@ void init_signals(void)
|
|||
sigaction(SIGABRT,&custom_sigaction,NULL);
|
||||
sigaction(SIGFPE,&custom_sigaction,NULL);
|
||||
sigaction(SIGBUS,&custom_sigaction,NULL);
|
||||
sigaction(SIGILL,&custom_sigaction,NULL);
|
||||
sigaction(SIGSEGV,&custom_sigaction,NULL);
|
||||
sigaction(SIGQUIT,&custom_sigaction,NULL);
|
||||
sigaction(SIGINT,&dump_sigaction,NULL);
|
||||
sigaction(SIGPIPE,&ign_sigaction,NULL);
|
||||
sigaction(SIGSEGV,&custom_sigaction,NULL);
|
||||
|
||||
#ifdef __APPLE__
|
||||
mach_initialize();
|
||||
#endif
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue