blob: 9edba91d30e906c7e77ff553d26e5274a6a8b9ce [file] [log] [blame]
/*
* Copyright (C) 2009 Pekka Enberg
*
* This file is released under the GPL version 2 with the following
* clarification and special exception:
*
* Linking this library statically or dynamically with other modules is
* making a combined work based on this library. Thus, the terms and
* conditions of the GNU General Public License cover the whole
* combination.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under terms
* of your choice, provided that you also meet, for each linked independent
* module, the terms and conditions of the license of that module. An
* independent module is a module which is not derived from or based on
* this library. If you modify this library, you may extend this exception
* to your version of the library, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
* Please refer to the file LICENSE for details.
*/
#include "jit/exception.h"
#include "vm/backtrace.h"
#include "vm/call.h"
#include "vm/class.h"
#include "vm/gc.h"
#include "vm/jni.h"
#include "vm/object.h"
#include "vm/preload.h"
#include "vm/signal.h"
#include "vm/stack-trace.h"
#include "vm/thread.h"
#include "sys/signal.h"
#include <ucontext.h>
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
static unsigned long throw_arithmetic_exception(unsigned long src_addr)
{
signal_new_exception(vm_java_lang_ArithmeticException,
"division by zero");
return throw_from_signal_bh(src_addr);
}
static unsigned long throw_null_pointer_exception(unsigned long src_addr)
{
signal_new_exception(vm_java_lang_NullPointerException, NULL);
return throw_from_signal_bh(src_addr);
}
static unsigned long throw_stack_overflow_error(unsigned long src_addr)
{
struct vm_object *obj;
obj = vm_alloc_stack_overflow_error();
if (!obj)
error("failed to allocate instance of StackOverflowError.");
signal_exception(obj);
return throw_from_signal_bh(src_addr);
}
static unsigned long rethrow_bh(unsigned long src_addr)
{
return throw_from_signal_bh(src_addr);
}
static void sigfpe_handler(int sig, siginfo_t *si, void *ctx)
{
if (signal_from_native(ctx))
goto exit;
if (si->si_code == FPE_INTDIV) {
if (install_signal_bh(ctx, throw_arithmetic_exception) == 0)
return;
fprintf(stderr, "%s: install_signal_bh() failed.\n", __func__);
}
exit:
print_backtrace_and_die(sig, si, ctx);
}
static void sigill_handler(int sig, siginfo_t *si, void *ctx)
{
print_backtrace_and_die(sig, si, ctx);
}
static void sigsegv_handler(int sig, siginfo_t *si, void *ctx)
{
if (signal_from_native(ctx))
goto exit;
/* Assume that zero-page access is caused by dereferencing a
null pointer */
if (!si->si_addr) {
/* We must be extra caucious here because IP might be
invalid */
if (get_signal_source_cu(ctx) == NULL)
goto exit;
if (install_signal_bh(ctx, throw_null_pointer_exception) == 0)
return;
fprintf(stderr, "%s: install_signal_bh() failed.\n", __func__);
goto exit;
}
/* Garbage collection safepoint */
if (si->si_addr == gc_safepoint_page) {
ucontext_t *uc = ctx;
save_signal_registers(&(vm_get_exec_env()->thread_register_state), &uc->uc_mcontext);
gc_safepoint(&(vm_get_exec_env()->thread_register_state));
return;
}
/* Check if exception was triggered by exception guard */
if (si->si_addr == exceptions_guard_page ||
si->si_addr == trampoline_exceptions_guard_page) {
struct vm_object *exception;
exception = exception_occurred();
if (exception == NULL) {
fprintf(stderr, "%s: spurious exception-test failure\n",
__func__);
goto exit;
}
if (si->si_addr == trampoline_exceptions_guard_page)
throw_from_trampoline(ctx, exception);
else
install_signal_bh(ctx, &rethrow_bh);
return;
}
/* Static field access */
if (si->si_addr == static_guard_page) {
install_signal_bh(ctx, &static_field_signal_bh);
return;
}
if (si->si_addr == jni_stack_badoffset ||
si->si_addr == vm_native_stack_badoffset) {
install_signal_bh(ctx, throw_stack_overflow_error);
return;
}
exit:
print_backtrace_and_die(sig, si, ctx);
}
static int main_called;
static void sigquit_handler(int sig, siginfo_t *si, void *ctx)
{
struct vm_thread *this;
print_trace();
if (main_called)
return;
main_called = true;
list_for_each_entry(this, &thread_list, list_node) {
if (this == vm_thread_self())
continue;
pthread_kill(this->posix_id, SIGQUIT);
}
}
void setup_signal_handlers(void)
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sa.sa_sigaction = sigsegv_handler;
sigaction(SIGSEGV, &sa, NULL);
sa.sa_sigaction = sigill_handler;
sigaction(SIGILL, &sa, NULL);
sa.sa_sigaction = sigfpe_handler;
sigaction(SIGFPE, &sa, NULL);
sa.sa_sigaction = sigquit_handler;
sigaction(SIGQUIT, &sa, NULL);
gc_setup_signals();
}