]> gitweb.factorcode.org Git - factor.git/commitdiff
implement mach exception handling
authorSlava Pestov <slava@factorcode.org>
Mon, 24 Oct 2005 00:50:29 +0000 (00:50 +0000)
committerSlava Pestov <slava@factorcode.org>
Mon, 24 Oct 2005 00:50:29 +0000 (00:50 +0000)
Makefile
TODO.FACTOR.txt
library/ui/world.factor
native/unix/mach_signal.c [new file with mode: 0644]
native/unix/mach_signal.h [new file with mode: 0644]
native/unix/signal.c

index 84f74dacd9abfadd0e1eca04b0c09d1acc0e9f86..38019b038cef2c520e2a09df41e377b3fd8778b4 100644 (file)
--- a/Makefile
+++ b/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 \
index 5e2dc0b8feac5f5379502271bb8e010b61b74ef0..d7651de47d5d7854da4a7687793114088949886f 100644 (file)
   it anyway?\r
 - apropos: use new smarter completion?\r
 - signal handler should not lose stack pointers\r
-- sigsegv handling on OS X:\r
-\r
-  http://developer.apple.com/technotes/tn2004/tn2123.html#SECLIMITATIONS\r
-  http://www.caddr.com/macho/archives/sbcl-devel/2005-3/4742.html\r
-  http://www.caddr.com/macho/archives/sbcl-devel/2005-3/4764.html\r
-  http://clozure.com/cgi-bin/viewcvs.cgi/ccl/lisp-kernel/lisp-exceptions.c?rev=1.9&content-type=text/vnd.viewcvs-markup\r
-\r
 - http keep alive, and range get\r
 \r
 + ffi:\r
index ff46b5564a7acef32c952069fc54da3fad397099..ed161a9c42acaa5411f1843ae2a537afecd59fee 100644 (file)
@@ -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 -- )
diff --git a/native/unix/mach_signal.c b/native/unix/mach_signal.c
new file mode 100644 (file)
index 0000000..8b52fec
--- /dev/null
@@ -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
diff --git a/native/unix/mach_signal.h b/native/unix/mach_signal.h
new file mode 100644 (file)
index 0000000..823d3fa
--- /dev/null
@@ -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
index 55ef9206298fab30df90ed90bde61f6a409767a8..44ebb0c0bdfd300f810c94b65361f8b297c7f2e8 100644 (file)
@@ -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
 }