diff options
Diffstat (limited to 'libsigsegv/src/handler-macos.c')
-rw-r--r-- | libsigsegv/src/handler-macos.c | 563 |
1 files changed, 0 insertions, 563 deletions
diff --git a/libsigsegv/src/handler-macos.c b/libsigsegv/src/handler-macos.c deleted file mode 100644 index 3a39e727..00000000 --- a/libsigsegv/src/handler-macos.c +++ /dev/null @@ -1,563 +0,0 @@ -/* 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; -} |