| /* 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 |
| } |