blob: ad29662d0fda46ceb2be2ca05130efb888bf9eb7 [file] [log] [blame]
/* go-signal.c -- signal handling for Go.
Copyright 2009 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <ucontext.h>
#include "runtime.h"
#ifndef SA_RESTART
#define SA_RESTART 0
#endif
#ifdef USING_SPLIT_STACK
extern void __splitstack_getcontext(void *context[10]);
extern void __splitstack_setcontext(void *context[10]);
extern void *__splitstack_find_context(void *context[10], size_t *,
void **, void **, void **);
#endif
// The rest of the signal handler, written in Go.
extern void sigtrampgo(uint32, siginfo_t *, void *)
__asm__(GOSYM_PREFIX "runtime.sigtrampgo");
// The Go signal handler, written in C. This should be running on the
// alternate signal stack. This is responsible for setting up the
// split stack context so that stack guard checks will work as
// expected.
void sigtramp(int, siginfo_t *, void *)
__attribute__ ((no_split_stack));
void sigtramp(int, siginfo_t *, void *)
__asm__ (GOSYM_PREFIX "runtime.sigtramp");
#ifndef USING_SPLIT_STACK
// When not using split stacks, there are no stack checks, and there
// is nothing special for this function to do.
void
sigtramp(int sig, siginfo_t *info, void *context)
{
sigtrampgo(sig, info, context);
}
#else // USING_SPLIT_STACK
void
sigtramp(int sig, siginfo_t *info, void *context)
{
G *gp;
void *stack_context[10];
void *stack;
size_t stack_size;
void *next_segment;
void *next_sp;
void *initial_sp;
uintptr sp;
stack_t st;
uintptr stsp;
gp = runtime_g();
if (gp == nil) {
// Let the Go code handle this case.
// It should only call nosplit functions in this case.
sigtrampgo(sig, info, context);
return;
}
// If this signal is one for which we will panic, we are not
// on the alternate signal stack. It's OK to call split-stack
// functions here.
if (sig == SIGBUS || sig == SIGFPE || sig == SIGSEGV) {
sigtrampgo(sig, info, context);
return;
}
// We are running on the alternate signal stack.
__splitstack_getcontext(&stack_context[0]);
stack = __splitstack_find_context((void*)(&gp->m->gsignal->stackcontext[0]),
&stack_size, &next_segment,
&next_sp, &initial_sp);
// If some non-Go code called sigaltstack, adjust.
sp = (uintptr)(&stack_size);
if (sp < (uintptr)(stack) || sp >= (uintptr)(stack) + stack_size) {
sigaltstack(nil, &st);
if ((st.ss_flags & SS_DISABLE) != 0) {
runtime_printf("signal %d received on thread with no signal stack\n", (int32)(sig));
runtime_throw("non-Go code disabled sigaltstack");
}
stsp = (uintptr)(st.ss_sp);
if (sp < stsp || sp >= stsp + st.ss_size) {
runtime_printf("signal %d received but handler not on signal stack\n", (int32)(sig));
runtime_throw("non-Go code set up signal handler without SA_ONSTACK flag");
}
// Unfortunately __splitstack_find_context will return NULL
// when it is called on a context that has never been used.
// There isn't much we can do but assume all is well.
if (stack != NULL) {
// Here the gc runtime adjusts the gsignal
// stack guard to match the values returned by
// sigaltstack. Unfortunately we have no way
// to do that.
runtime_printf("signal %d received on unknown signal stack\n", (int32)(sig));
runtime_throw("non-Go code changed signal stack");
}
}
// Set the split stack context so that the stack guards are
// checked correctly.
__splitstack_setcontext((void*)(&gp->m->gsignal->stackcontext[0]));
sigtrampgo(sig, info, context);
// We are going to return back to the signal trampoline and
// then to whatever we were doing before we got the signal.
// Restore the split stack context so that stack guards are
// checked correctly.
__splitstack_setcontext(&stack_context[0]);
}
#endif // USING_SPLIT_STACK
// C function to return the address of the sigtramp function.
uintptr getSigtramp(void) __asm__ (GOSYM_PREFIX "runtime.getSigtramp");
uintptr
getSigtramp()
{
return (uintptr)(void*)sigtramp;
}
// C code to manage the sigaction sa_sigaction field, which is
// typically a union and so hard for mksysinfo.sh to handle.
uintptr getSigactionHandler(struct sigaction*)
__attribute__ ((no_split_stack));
uintptr getSigactionHandler(struct sigaction*)
__asm__ (GOSYM_PREFIX "runtime.getSigactionHandler");
uintptr
getSigactionHandler(struct sigaction* sa)
{
return (uintptr)(sa->sa_sigaction);
}
void setSigactionHandler(struct sigaction*, uintptr)
__attribute__ ((no_split_stack));
void setSigactionHandler(struct sigaction*, uintptr)
__asm__ (GOSYM_PREFIX "runtime.setSigactionHandler");
void
setSigactionHandler(struct sigaction* sa, uintptr handler)
{
sa->sa_sigaction = (void*)(handler);
}
// C code to fetch values from the siginfo_t and ucontext_t pointers
// passed to a signal handler.
struct getSiginfoRet {
uintptr sigaddr;
uintptr sigpc;
};
struct getSiginfoRet getSiginfo(siginfo_t *, void *)
__asm__(GOSYM_PREFIX "runtime.getSiginfo");
struct getSiginfoRet
getSiginfo(siginfo_t *info, void *context __attribute__((unused)))
{
struct getSiginfoRet ret;
Location loc[1];
int32 n;
if (info == nil) {
ret.sigaddr = 0;
} else {
ret.sigaddr = (uintptr)(info->si_addr);
}
ret.sigpc = 0;
// There doesn't seem to be a portable way to get the PC.
// Use unportable code to pull it from context, and if that fails
// try a stack backtrace across the signal handler.
#ifdef __x86_64__
#ifdef __linux__
ret.sigpc = ((ucontext_t*)(context))->uc_mcontext.gregs[REG_RIP];
#endif
#endif
#ifdef __i386__
#ifdef __linux__
ret.sigpc = ((ucontext_t*)(context))->uc_mcontext.gregs[REG_EIP];
#endif
#endif
if (ret.sigpc == 0) {
// Skip getSiginfo/sighandler/sigtrampgo/sigtramp/handler.
n = runtime_callers(5, &loc[0], 1, false);
if (n > 0) {
ret.sigpc = loc[0].pc;
}
}
return ret;
}
// Dump registers when crashing in a signal.
// There is no portable way to write this,
// so we just have some CPU/OS specific implementations.
void dumpregs(siginfo_t *, void *)
__asm__(GOSYM_PREFIX "runtime.dumpregs");
void
dumpregs(siginfo_t *info __attribute__((unused)), void *context __attribute__((unused)))
{
#ifdef __x86_64__
#ifdef __linux__
{
mcontext_t *m = &((ucontext_t*)(context))->uc_mcontext;
runtime_printf("rax %X\n", m->gregs[REG_RAX]);
runtime_printf("rbx %X\n", m->gregs[REG_RBX]);
runtime_printf("rcx %X\n", m->gregs[REG_RCX]);
runtime_printf("rdx %X\n", m->gregs[REG_RDX]);
runtime_printf("rdi %X\n", m->gregs[REG_RDI]);
runtime_printf("rsi %X\n", m->gregs[REG_RSI]);
runtime_printf("rbp %X\n", m->gregs[REG_RBP]);
runtime_printf("rsp %X\n", m->gregs[REG_RSP]);
runtime_printf("r8 %X\n", m->gregs[REG_R8]);
runtime_printf("r9 %X\n", m->gregs[REG_R9]);
runtime_printf("r10 %X\n", m->gregs[REG_R10]);
runtime_printf("r11 %X\n", m->gregs[REG_R11]);
runtime_printf("r12 %X\n", m->gregs[REG_R12]);
runtime_printf("r13 %X\n", m->gregs[REG_R13]);
runtime_printf("r14 %X\n", m->gregs[REG_R14]);
runtime_printf("r15 %X\n", m->gregs[REG_R15]);
runtime_printf("rip %X\n", m->gregs[REG_RIP]);
runtime_printf("rflags %X\n", m->gregs[REG_EFL]);
runtime_printf("cs %X\n", m->gregs[REG_CSGSFS] & 0xffff);
runtime_printf("fs %X\n", (m->gregs[REG_CSGSFS] >> 16) & 0xffff);
runtime_printf("gs %X\n", (m->gregs[REG_CSGSFS] >> 32) & 0xffff);
}
#endif
#endif
#ifdef __i386__
#ifdef __linux__
{
mcontext_t *m = &((ucontext_t*)(context))->uc_mcontext;
runtime_printf("eax %X\n", m->gregs[REG_EAX]);
runtime_printf("ebx %X\n", m->gregs[REG_EBX]);
runtime_printf("ecx %X\n", m->gregs[REG_ECX]);
runtime_printf("edx %X\n", m->gregs[REG_EDX]);
runtime_printf("edi %X\n", m->gregs[REG_EDI]);
runtime_printf("esi %X\n", m->gregs[REG_ESI]);
runtime_printf("ebp %X\n", m->gregs[REG_EBP]);
runtime_printf("esp %X\n", m->gregs[REG_ESP]);
runtime_printf("eip %X\n", m->gregs[REG_EIP]);
runtime_printf("eflags %X\n", m->gregs[REG_EFL]);
runtime_printf("cs %X\n", m->gregs[REG_CS]);
runtime_printf("fs %X\n", m->gregs[REG_FS]);
runtime_printf("gs %X\n", m->gregs[REG_GS]);
}
#endif
#endif
}