diff options
Diffstat (limited to 'contrib/llvm/lib/Support/Unix/Signals.inc')
-rw-r--r-- | contrib/llvm/lib/Support/Unix/Signals.inc | 148 |
1 files changed, 101 insertions, 47 deletions
diff --git a/contrib/llvm/lib/Support/Unix/Signals.inc b/contrib/llvm/lib/Support/Unix/Signals.inc index 061cdb3..55fd76d 100644 --- a/contrib/llvm/lib/Support/Unix/Signals.inc +++ b/contrib/llvm/lib/Support/Unix/Signals.inc @@ -45,6 +45,17 @@ #if HAVE_LINK_H #include <link.h> #endif +#if HAVE_UNWIND_BACKTRACE +// FIXME: We should be able to use <unwind.h> for any target that has an +// _Unwind_Backtrace function, but on FreeBSD the configure test passes +// despite the function not existing, and on Android, <unwind.h> conflicts +// with <link.h>. +#ifdef __GLIBC__ +#include <unwind.h> +#else +#undef HAVE_UNWIND_BACKTRACE +#endif +#endif using namespace llvm; @@ -57,6 +68,8 @@ static void (*InterruptFunction)() = nullptr; static ManagedStatic<std::vector<std::string>> FilesToRemove; +static StringRef Argv0; + // IntSigs - Signals that represent requested termination. There's no bug // or failure, or if there is, it's not our direct responsibility. For whatever // reason, our continued execution is no longer desirable. @@ -96,7 +109,7 @@ static void RegisterHandler(int Signal) { struct sigaction NewHandler; NewHandler.sa_handler = SignalHandler; - NewHandler.sa_flags = SA_NODEFER|SA_RESETHAND; + NewHandler.sa_flags = SA_NODEFER | SA_RESETHAND | SA_ONSTACK; sigemptyset(&NewHandler.sa_mask); // Install the new handler, save the old one in RegisteredSignalInfo. @@ -106,6 +119,35 @@ static void RegisterHandler(int Signal) { ++NumRegisteredSignals; } +#if defined(HAVE_SIGALTSTACK) +// Hold onto the old alternate signal stack so that it's not reported as a leak. +// We don't make any attempt to remove our alt signal stack if we remove our +// signal handlers; that can't be done reliably if someone else is also trying +// to do the same thing. +static stack_t OldAltStack; + +static void CreateSigAltStack() { + const size_t AltStackSize = MINSIGSTKSZ + 8192; + + // If we're executing on the alternate stack, or we already have an alternate + // signal stack that we're happy with, there's nothing for us to do. Don't + // reduce the size, some other part of the process might need a larger stack + // than we do. + if (sigaltstack(nullptr, &OldAltStack) != 0 || + OldAltStack.ss_flags & SS_ONSTACK || + (OldAltStack.ss_sp && OldAltStack.ss_size >= AltStackSize)) + return; + + stack_t AltStack = {}; + AltStack.ss_sp = reinterpret_cast<char *>(malloc(AltStackSize)); + AltStack.ss_size = AltStackSize; + if (sigaltstack(&AltStack, &OldAltStack) != 0) + free(AltStack.ss_sp); +} +#else +static void CreateSigAltStack() {} +#endif + static void RegisterHandlers() { // We need to dereference the signals mutex during handler registration so // that we force its construction. This is to prevent the first use being @@ -116,6 +158,10 @@ static void RegisterHandlers() { // If the handlers are already registered, we're done. if (NumRegisteredSignals != 0) return; + // Create an alternate stack for signal handling. This is necessary for us to + // be able to reliably handle signals due to stack overflow. + CreateSigAltStack(); + for (auto S : IntSigs) RegisterHandler(S); for (auto S : KillSigs) RegisterHandler(S); } @@ -309,20 +355,64 @@ static bool findModulesAndOffsets(void **StackTrace, int Depth, } #endif // defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) && ... +#if defined(ENABLE_BACKTRACES) && defined(HAVE_UNWIND_BACKTRACE) +static int unwindBacktrace(void **StackTrace, int MaxEntries) { + if (MaxEntries < 0) + return 0; + + // Skip the first frame ('unwindBacktrace' itself). + int Entries = -1; + + auto HandleFrame = [&](_Unwind_Context *Context) -> _Unwind_Reason_Code { + // Apparently we need to detect reaching the end of the stack ourselves. + void *IP = (void *)_Unwind_GetIP(Context); + if (!IP) + return _URC_END_OF_STACK; + + assert(Entries < MaxEntries && "recursively called after END_OF_STACK?"); + if (Entries >= 0) + StackTrace[Entries] = IP; + + if (++Entries == MaxEntries) + return _URC_END_OF_STACK; + return _URC_NO_REASON; + }; + + _Unwind_Backtrace( + [](_Unwind_Context *Context, void *Handler) { + return (*static_cast<decltype(HandleFrame) *>(Handler))(Context); + }, + static_cast<void *>(&HandleFrame)); + return std::max(Entries, 0); +} +#endif + // PrintStackTrace - In the case of a program crash or fault, print out a stack // trace so that the user has an indication of why and where we died. // // On glibc systems we have the 'backtrace' function, which works nicely, but // doesn't demangle symbols. void llvm::sys::PrintStackTrace(raw_ostream &OS) { -#if defined(HAVE_BACKTRACE) && defined(ENABLE_BACKTRACES) - static void* StackTrace[256]; +#if defined(ENABLE_BACKTRACES) + static void *StackTrace[256]; + int depth = 0; +#if defined(HAVE_BACKTRACE) // Use backtrace() to output a backtrace on Linux systems with glibc. - int depth = backtrace(StackTrace, + if (!depth) + depth = backtrace(StackTrace, static_cast<int>(array_lengthof(StackTrace))); +#endif +#if defined(HAVE_UNWIND_BACKTRACE) + // Try _Unwind_Backtrace() if backtrace() failed. + if (!depth) + depth = unwindBacktrace(StackTrace, static_cast<int>(array_lengthof(StackTrace))); - if (printSymbolizedStackTrace(StackTrace, depth, OS)) +#endif + if (!depth) + return; + + if (printSymbolizedStackTrace(Argv0, StackTrace, depth, OS)) return; -#if HAVE_DLFCN_H && __GNUG__ +#if HAVE_DLFCN_H && __GNUG__ && !defined(__CYGWIN__) int width = 0; for (int i = 0; i < depth; ++i) { Dl_info dlinfo; @@ -369,7 +459,7 @@ void llvm::sys::PrintStackTrace(raw_ostream &OS) { } OS << '\n'; } -#else +#elif defined(HAVE_BACKTRACE) backtrace_symbols_fd(StackTrace, depth, STDERR_FILENO); #endif #endif @@ -383,7 +473,10 @@ void llvm::sys::DisableSystemDialogsOnCrash() {} /// PrintStackTraceOnErrorSignal - When an error signal (such as SIGABRT or /// SIGSEGV) is delivered to the process, print a stack trace and then exit. -void llvm::sys::PrintStackTraceOnErrorSignal(bool DisableCrashReporting) { +void llvm::sys::PrintStackTraceOnErrorSignal(StringRef Argv0, + bool DisableCrashReporting) { + ::Argv0 = Argv0; + AddSignalHandler(PrintStackTraceSignalHandler, nullptr); #if defined(__APPLE__) && defined(ENABLE_CRASH_OVERRIDES) @@ -402,42 +495,3 @@ void llvm::sys::PrintStackTraceOnErrorSignal(bool DisableCrashReporting) { } #endif } - - -/***/ - -// On Darwin, raise sends a signal to the main thread instead of the current -// thread. This has the unfortunate effect that assert() and abort() will end up -// bypassing our crash recovery attempts. We work around this for anything in -// the same linkage unit by just defining our own versions of the assert handler -// and abort. - -#if defined(__APPLE__) && defined(ENABLE_CRASH_OVERRIDES) - -#include <signal.h> -#include <pthread.h> - -int raise(int sig) { - return pthread_kill(pthread_self(), sig); -} - -void __assert_rtn(const char *func, - const char *file, - int line, - const char *expr) { - if (func) - fprintf(stderr, "Assertion failed: (%s), function %s, file %s, line %d.\n", - expr, func, file, line); - else - fprintf(stderr, "Assertion failed: (%s), file %s, line %d.\n", - expr, file, line); - abort(); -} - -void abort() { - raise(SIGABRT); - usleep(1000); - __builtin_trap(); -} - -#endif |