/* 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 #include #include #include #include #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(&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(&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 }