diff options
Diffstat (limited to 'asmrun/signals_asm.c')
-rw-r--r-- | asmrun/signals_asm.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/asmrun/signals_asm.c b/asmrun/signals_asm.c new file mode 100644 index 000000000..639fbd4d1 --- /dev/null +++ b/asmrun/signals_asm.c @@ -0,0 +1,256 @@ +/***********************************************************************/ +/* */ +/* Objective Caml */ +/* */ +/* Xavier Leroy, projet Gallium, INRIA Rocquencourt */ +/* */ +/* Copyright 2007 Institut National de Recherche en Informatique et */ +/* en Automatique. All rights reserved. This file is distributed */ +/* under the terms of the GNU Library General Public License, with */ +/* the special exception on linking described in file ../LICENSE. */ +/* */ +/***********************************************************************/ + +/* $Id$ */ + +/* Signal handling, code specific to the native-code compiler */ + +#if defined(TARGET_amd64) && defined (SYS_linux) +#define _GNU_SOURCE +#endif +#include <signal.h> +#include <stdio.h> +#include "fail.h" +#include "memory.h" +#include "osdeps.h" +#include "signals.h" +#include "signals_machdep.h" +#include "signals_osdep.h" +#include "stack.h" + +#ifdef HAS_STACK_OVERFLOW_DETECTION +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#ifndef NSIG +#define NSIG 64 +#endif + +typedef void (*signal_handler)(int signo); + +#ifdef _WIN32 +extern signal_handler caml_win32_signal(int sig, signal_handler action); +#define signal(sig,act) caml_win32_signal(sig,act) +extern void caml_win32_overflow_detection(); +#endif + +extern char * caml_code_area_start, * caml_code_area_end; + +#define In_code_area(pc) \ + ((char *)(pc) >= caml_code_area_start && \ + (char *)(pc) <= caml_code_area_end) + +/* This routine is the common entry point for garbage collection + and signal handling. It can trigger a callback to Caml code. + With system threads, this callback can cause a context switch. + Hence [caml_garbage_collection] must not be called from regular C code + (e.g. the [caml_alloc] function) because the context of the call + (e.g. [intern_val]) may not allow context switching. + Only generated assembly code can call [caml_garbage_collection], + via the caml_call_gc assembly stubs. */ + +void caml_garbage_collection(void) +{ + caml_young_limit = caml_young_start; + if (caml_young_ptr < caml_young_start || caml_force_major_slice) { + caml_minor_collection(); + } + caml_process_pending_signals(); +} + +DECLARE_SIGNAL_HANDLER(handle_signal) +{ +#if !defined(POSIX_SIGNALS) && !defined(BSD_SIGNALS) + signal(sig, handle_signal); +#endif + if (sig < 0 || sig >= NSIG) return; + if (caml_try_leave_blocking_section_hook ()) { + caml_execute_signal(sig, 1); + caml_enter_blocking_section_hook(); + } else { + caml_record_signal(sig); + /* Some ports cache [caml_young_limit] in a register. + Use the signal context to modify that register too, but only if + we are inside Caml code (not inside C code). */ +#if defined(CONTEXT_PC) && defined(CONTEXT_YOUNG_LIMIT) + if (In_code_area(CONTEXT_PC)) + CONTEXT_YOUNG_LIMIT = (context_reg) caml_young_limit; +#endif + } +} + +int caml_set_signal_action(int signo, int action) +{ + signal_handler oldact; +#ifdef POSIX_SIGNALS + struct sigaction sigact, oldsigact; +#else + signal_handler act; +#endif + +#ifdef POSIX_SIGNALS + switch(action) { + case 0: + sigact.sa_handler = SIG_DFL; + sigact.sa_flags = 0; + break; + case 1: + sigact.sa_handler = SIG_IGN; + sigact.sa_flags = 0; + break; + default: + SET_SIGACT(sigact, handle_signal); + break; + } + sigemptyset(&sigact.sa_mask); + if (sigaction(signo, &sigact, &oldsigact) == -1) return -1; + oldact = oldsigact.sa_handler; +#else + switch(action) { + case 0: act = SIG_DFL; break; + case 1: act = SIG_IGN; break; + default: act = handle_signal; break; + } + oldact = signal(signo, act); + if (oldact == SIG_ERR) return -1; +#endif + if (oldact == (signal_handler) handle_signal) + return 2; + else if (oldact == SIG_IGN) + return 1; + else + return 0; +} + +/* Machine- and OS-dependent handling of bound check trap */ + +#if defined(TARGET_power) || (defined(TARGET_sparc) && defined(SYS_solaris)) +DECLARE_SIGNAL_HANDLER(trap_handler) +{ +#if defined(SYS_solaris) + if (info->si_code != ILL_ILLTRP) { + /* Deactivate our exception handler and return. */ + struct sigaction act; + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + sigaction(sig, &act, NULL); + return; + } +#endif +#if defined(SYS_rhapsody) + /* Unblock SIGTRAP */ + { sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGTRAP); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + } +#endif + caml_exception_pointer = (char *) CONTEXT_EXCEPTION_POINTER; + caml_young_ptr = (char *) CONTEXT_YOUNG_PTR; +#if defined(SYS_rhapsody) + caml_bottom_of_stack = (char *) CONTEXT_SP; + caml_last_return_address = (uintnat) CONTEXT_PC; +#endif + caml_array_bound_error(); +} +#endif + +/* Machine- and OS-dependent handling of stack overflow */ + +#ifdef HAS_STACK_OVERFLOW_DETECTION + +static char * system_stack_top; +static char sig_alt_stack[SIGSTKSZ]; + +DECLARE_SIGNAL_HANDLER(segv_handler) +{ + struct rlimit limit; + struct sigaction act; + char * fault_addr; + + /* Sanity checks: + - faulting address is word-aligned + - faulting address is within the stack + - we are in Caml code */ + fault_addr = CONTEXT_FAULTING_ADDRESS; + if (((uintnat) fault_addr & (sizeof(intnat) - 1)) == 0 + && getrlimit(RLIMIT_STACK, &limit) == 0 + && fault_addr < system_stack_top + && fault_addr >= system_stack_top - limit.rlim_cur - 0x2000 +#ifdef CONTEXT_PC + && In_code_area(CONTEXT_PC) +#endif + ) { + /* Turn this into a Stack_overflow exception */ +#if defined(CONTEXT_YOUNG_PTR) && defined(CONTEXT_EXCEPTION_POINTER) + caml_exception_pointer = (char *) CONTEXT_EXCEPTION_POINTER; + caml_young_ptr = (char *) CONTEXT_YOUNG_PTR; +#endif + caml_raise_stack_overflow(); + } + /* Otherwise, deactivate our exception handler and return, + causing fatal signal to be generated at point of error. */ + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + sigaction(SIGSEGV, &act, NULL); +} + +#endif + +/* Initialization of signal stuff */ + +void caml_init_signals(void) +{ + /* Bound-check trap handling */ +#if defined(TARGET_sparc) && defined(SYS_solaris) + { struct sigaction act; + sigemptyset(&act.sa_mask); + SET_SIGACT(act, trap_handler); + act.sa_flags |= SA_NODEFER; + sigaction(SIGILL, &act, NULL); + } +#endif + +#if defined(TARGET_power) + { struct sigaction act; + sigemptyset(&act.sa_mask); + SET_SIGACT(act, trap_handler); +#if !defined(SYS_rhapsody) + act.sa_flags |= SA_NODEFER; +#endif + sigaction(SIGTRAP, &act, NULL); + } +#endif + + /* Stack overflow handling */ +#ifdef HAS_STACK_OVERFLOW_DETECTION + { + struct sigaltstack stk; + struct sigaction act; + stk.ss_sp = sig_alt_stack; + stk.ss_size = SIGSTKSZ; + stk.ss_flags = 0; + SET_SIGACT(act, segv_handler); + act.sa_flags |= SA_ONSTACK | SA_NODEFER; + sigemptyset(&act.sa_mask); + system_stack_top = (char *) &act; + if (sigaltstack(&stk, NULL) == 0) { sigaction(SIGSEGV, &act, NULL); } + } +#endif +#ifdef _WIN32 + caml_win32_overflow_detection(); +#endif +} |