aboutsummaryrefslogtreecommitdiffstats
path: root/libsigsegv/src/handler-macos.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2010-07-16 14:52:31 +0300
committerArnold D. Robbins <arnold@skeeve.com>2010-07-16 14:52:31 +0300
commit3ba50a15ebd976f7a88393e2e45dc14b6478b9a9 (patch)
tree6a6bbe6bed1141051fefe94b2d39eacd4854235a /libsigsegv/src/handler-macos.c
parent6a2caf2157d87b4b582b2494bdd7d6a688dd0b1f (diff)
downloadegawk-3ba50a15ebd976f7a88393e2e45dc14b6478b9a9.tar.gz
egawk-3ba50a15ebd976f7a88393e2e45dc14b6478b9a9.tar.bz2
egawk-3ba50a15ebd976f7a88393e2e45dc14b6478b9a9.zip
Move to gawk-3.1.7.
Diffstat (limited to 'libsigsegv/src/handler-macos.c')
-rw-r--r--libsigsegv/src/handler-macos.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/libsigsegv/src/handler-macos.c b/libsigsegv/src/handler-macos.c
new file mode 100644
index 00000000..3a39e727
--- /dev/null
+++ b/libsigsegv/src/handler-macos.c
@@ -0,0 +1,563 @@
+/* Fault handler information. MacOSX version.
+ Copyright (C) 1993-1999, 2002-2003, 2007-2008 Bruno Haible <bruno@clisp.org>
+ Copyright (C) 2003 Paolo Bonzini <bonzini@gnu.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include "sigsegv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#if HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+
+#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
+
+/* In the header files of MacOS X >= 10.5, when compiling with flags that lead
+ to __DARWIN_UNIX03=1 (see <sys/cdefs.h>), the register names are prefixed
+ with '__'. To test for MacOS X >= 10.5 versus < 10.5, we cannot use a
+ predefined macro such as __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
+ because that does not change when a cross-compile via -isysroot is
+ activated. Instead use some macro defined inside the header files and which
+ changed in 10.5, such as
+ File Macro 10.4 10.5
+ <mach/machine/exception.h> EXC_TYPES_COUNT 10 11
+ <mach/exception_types.h> EXC_CRASH -- 10
+ <mach/mach_vm.h> mach_vm_MSG_COUNT 18 19
+ <mach/machine.h> CPU_TYPE_ARM -- ...
+ <mach/memory_object_control.h> memory_object_control_MSG_COUNT 11 12
+ <mach/memory_object_types.h> UPL_ABORT_REFERENCE -- 0x80
+ <mach/message.h> MACH_RCV_TRAILER_AV -- 8
+ <mach/port.h> MACH_PORT_RIGHT_LABELH -- ...
+ <mach/thread_policy.h> THREAD_AFFINITY_POLICY -- 4
+ <mach/vm_region.h> VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ...
+ */
+#if EXC_TYPES_COUNT >= 11
+# define MacOS_X_10_5_HEADERS 1
+#endif
+
+#include "machfault.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 */
+
+/* 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);
+
+
+/* Our exception thread. */
+static mach_port_t our_exception_thread;
+
+/* The exception port on which our thread listens. */
+static mach_port_t our_exception_port;
+
+
+/* mach_initialize() status:
+ 0: not yet called
+ 1: called and succeeded
+ -1: called and failed */
+static int mach_initialized = 0;
+
+/* Communication area for the exception state and thread state. */
+static SIGSEGV_THREAD_STATE_TYPE save_thread_state;
+
+/* Check for reentrant signals. */
+static int emergency = -1;
+
+/* User's stack overflow handler. */
+static stackoverflow_handler_t stk_user_handler = (stackoverflow_handler_t)NULL;
+static unsigned long stk_extra_stack;
+static unsigned long stk_extra_stack_size;
+
+/* User's fault handler. */
+static sigsegv_handler_t user_handler = (sigsegv_handler_t)NULL;
+
+/* Thread that signalled the exception. Only set while user_handler is being
+ invoked. */
+static mach_port_t signalled_thread = (mach_port_t) 0;
+
+/* A handler that is called in the faulting thread. It terminates the thread. */
+static void
+terminating_handler ()
+{
+ /* Dump core. */
+ raise (SIGSEGV);
+
+ /* Seriously. */
+ abort ();
+}
+
+/* A handler that is called in the faulting thread, on an alternate stack.
+ It calls the user installed stack overflow handler. */
+static void
+altstack_handler ()
+{
+ /* We arrive here when the user refused to handle a fault. */
+
+ /* Check if it is plausibly a stack overflow, and the user installed
+ a stack overflow handler. */
+ if (stk_user_handler)
+ {
+ emergency++;
+ /* Call user's handler. */
+ (*stk_user_handler) (emergency, &save_thread_state);
+ }
+
+ /* Else, terminate the thread. */
+ terminating_handler ();
+}
+
+
+/* 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;
+
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Exception: 0x%x Code: 0x%x 0x%x in catch....\n",
+ exception,
+ code_count > 0 ? code[0] : -1,
+ code_count > 1 ? code[1] : -1);
+#endif
+
+ /* 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. */
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "thread_get_state failed for exception state\n");
+#endif
+ 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. */
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "thread_get_state failed for thread state\n");
+#endif
+ 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. */
+ if (addr <= sp + 4096 && sp <= addr + 4096)
+ {
+ unsigned long new_safe_esp;
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Treating as stack overflow, sp = 0x%lx\n", (char *) sp);
+#endif
+ new_safe_esp =
+#if STACK_DIRECTION < 0
+ stk_extra_stack + stk_extra_stack_size - 256;
+#else
+ stk_extra_stack + 256;
+#endif
+#if defined __x86_64__ || defined __i386__
+ new_safe_esp &= -16; /* align */
+ new_safe_esp -= sizeof (void *); /* make room for (unused) return address slot */
+#endif
+ SIGSEGV_STACK_POINTER (thread_state) = new_safe_esp;
+ /* Continue handling this fault in the faulting thread. (We cannot longjmp while
+ in the exception handling thread, so we need to mimic what signals do!) */
+ SIGSEGV_PROGRAM_COUNTER (thread_state) = (unsigned long) altstack_handler;
+ }
+ else
+ {
+ if (user_handler)
+ {
+ int done;
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Calling user handler, addr = 0x%lx\n", (char *) addr);
+#endif
+ signalled_thread = thread;
+ done = (*user_handler) ((void *) addr, 1);
+ signalled_thread = (mach_port_t) 0;
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Back from user handler\n");
+#endif
+ if (done)
+ return KERN_SUCCESS;
+ }
+ 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)
+ {
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "thread_set_state failed for altstack state\n");
+#endif
+ return KERN_FAILURE;
+ }
+ return KERN_SUCCESS;
+}
+
+
+/* The main function of the thread listening for exceptions. */
+static void *
+mach_exception_thread (void *arg)
+{
+ /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_thread_self.html. */
+ our_exception_thread = mach_thread_self ();
+
+ 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;
+
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Exception thread going to sleep\n");
+#endif
+
+ /* 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);
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Exception thread woke up\n");
+#endif
+ if (retval != MACH_MSG_SUCCESS)
+ {
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "mach_msg receive failed with %d %s\n",
+ (int) retval, mach_error_string (retval));
+#endif
+ abort ();
+ }
+
+ /* Handle the message: Call exc_server, which will call
+ catch_exception_raise and produce a reply message. */
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Calling exc_server\n");
+#endif
+ exc_server (&msg.head, &reply.head);
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Finished exc_server\n");
+#endif
+
+ /* 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)
+ {
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "mach_msg send failed\n");
+#endif
+ abort ();
+ }
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "Reply successful\n");
+#endif
+ }
+}
+
+
+/* Initialize the Mach exception handler thread.
+ Return 0 if OK, -1 on error. */
+static 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;
+}
+
+
+int
+sigsegv_install_handler (sigsegv_handler_t handler)
+{
+ if (!mach_initialized)
+ mach_initialized = (mach_initialize () >= 0 ? 1 : -1);
+ if (mach_initialized < 0)
+ return -1;
+
+ user_handler = handler;
+
+ return 0;
+}
+
+void
+sigsegv_deinstall_handler (void)
+{
+ user_handler = (sigsegv_handler_t)NULL;
+}
+
+int
+sigsegv_leave_handler (void (*continuation) (void*, void*, void*),
+ void* cont_arg1, void* cont_arg2, void* cont_arg3)
+{
+ emergency--;
+ if (mach_thread_self () == our_exception_thread)
+ {
+ /* Inside user_handler invocation. */
+ mach_port_t thread;
+ SIGSEGV_THREAD_STATE_TYPE thread_state;
+ mach_msg_type_number_t state_count;
+
+ thread = signalled_thread;
+ if (thread == (mach_port_t) 0)
+ {
+ /* The variable signalled_thread was supposed to be set! */
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "sigsegv_leave_handler: signalled_thread not set\n");
+#endif
+ return 0;
+ }
+
+ /* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html. */
+ state_count = SIGSEGV_THREAD_STATE_COUNT;
+ if (thread_get_state (thread, SIGSEGV_THREAD_STATE_FLAVOR,
+ (void *) &thread_state, &state_count)
+ != KERN_SUCCESS)
+ {
+ /* The thread was supposed to be suspended! */
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "sigsegv_leave_handler: thread_get_state failed for thread state\n");
+#endif
+ return 0;
+ }
+
+#if defined __ppc64__ || defined __ppc__ || defined __x86_64__
+ /* Store arguments in registers. */
+ SIGSEGV_INTEGER_ARGUMENT_1 (thread_state) = (unsigned long) cont_arg1;
+ SIGSEGV_INTEGER_ARGUMENT_2 (thread_state) = (unsigned long) cont_arg2;
+ SIGSEGV_INTEGER_ARGUMENT_3 (thread_state) = (unsigned long) cont_arg3;
+#endif
+#if defined __x86_64__
+ /* Align stack. */
+ {
+ unsigned long new_esp = SIGSEGV_STACK_POINTER (thread_state);
+ new_esp &= -16; /* align */
+ new_esp -= sizeof (void *); *(void **)new_esp = SIGSEGV_FRAME_POINTER (thread_state); /* push %rbp */
+ SIGSEGV_STACK_POINTER (thread_state) = new_esp;
+ SIGSEGV_FRAME_POINTER (thread_state) = new_esp; /* mov %rsp,%rbp */
+ }
+#elif defined __i386__
+ /* Push arguments onto the stack. */
+ {
+ unsigned long new_esp = SIGSEGV_STACK_POINTER (thread_state);
+ new_esp &= -16; /* align */
+ new_esp -= sizeof (void *); /* unused room, alignment */
+ new_esp -= sizeof (void *); *(void **)new_esp = cont_arg3;
+ new_esp -= sizeof (void *); *(void **)new_esp = cont_arg2;
+ new_esp -= sizeof (void *); *(void **)new_esp = cont_arg1;
+ new_esp -= sizeof (void *); /* make room for (unused) return address slot */
+ SIGSEGV_STACK_POINTER (thread_state) = new_esp;
+ }
+#endif
+ /* Point program counter to continuation to be executed. */
+ SIGSEGV_PROGRAM_COUNTER (thread_state) = (unsigned long) continuation;
+
+ /* 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)
+ {
+#ifdef DEBUG_EXCEPTION_HANDLING
+ fprintf (stderr, "sigsegv_leave_handler: thread_set_state failed\n");
+#endif
+ return 0;
+ }
+
+ return 1;
+ }
+ else
+ {
+ /* Inside stk_user_handler invocation. Stay in the same thread. */
+ (*continuation) (cont_arg1, cont_arg2, cont_arg3);
+ return 1;
+ }
+}
+
+int
+stackoverflow_install_handler (stackoverflow_handler_t handler,
+ void *extra_stack, unsigned long extra_stack_size)
+{
+ if (!mach_initialized)
+ mach_initialized = (mach_initialize () >= 0 ? 1 : -1);
+ if (mach_initialized < 0)
+ return -1;
+
+ stk_user_handler = handler;
+ stk_extra_stack = (unsigned long) extra_stack;
+ stk_extra_stack_size = extra_stack_size;
+ return 0;
+}
+
+void
+stackoverflow_deinstall_handler (void)
+{
+ stk_user_handler = (stackoverflow_handler_t) NULL;
+}