diff --git a/runtime/vm/signal_handler.h b/runtime/vm/signal_handler.h index b204c55d55f..ecdfe02ccc3 100644 --- a/runtime/vm/signal_handler.h +++ b/runtime/vm/signal_handler.h @@ -36,6 +36,17 @@ struct sigset_t { }; #endif + +// Old linux kernels on ARM might require a trampoline to +// work around incorrect Thumb -> ARM transitions. See SignalHandlerTrampoline +// below for more details. +#if defined(HOST_ARCH_ARM) && \ + (defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID)) && \ + !defined(__thumb__) +#define USE_SIGNAL_HANDLER_TRAMPOLINE +#endif + + namespace dart { typedef void (*SignalAction)(int signal, siginfo_t* info, @@ -43,17 +54,68 @@ typedef void (*SignalAction)(int signal, siginfo_t* info, class SignalHandler : public AllStatic { public: - static void Install(SignalAction action); + template + static void Install() { +#if defined(USE_SIGNAL_HANDLER_TRAMPOLINE) + InstallImpl(SignalHandlerTrampoline); +#else + InstallImpl(action); +#endif // defined(USE_SIGNAL_HANDLER_TRAMPOLINE) + } static void Remove(); static uintptr_t GetProgramCounter(const mcontext_t& mcontext); static uintptr_t GetFramePointer(const mcontext_t& mcontext); static uintptr_t GetCStackPointer(const mcontext_t& mcontext); static uintptr_t GetDartStackPointer(const mcontext_t& mcontext); static uintptr_t GetLinkRegister(const mcontext_t& mcontext); + private: + static void InstallImpl(SignalAction action); + +#if defined(USE_SIGNAL_HANDLER_TRAMPOLINE) + // Work around for a bug in old kernels (only fixed in 3.18 Android kernel): + // + // Kernel does not clear If-Then execution state bits when entering ARM signal + // handler which violates requirements imposed by ARM architecture reference. + // Some CPUs look at these bits even while in ARM mode which causes them + // to skip some instructions in the prologue of the signal handler. + // + // To work around the issue we insert enough NOPs in the prologue to ensure + // that no actual instructions are skipped and then branch to the actual + // signal handler. + // + // For the kernel patch that fixes the issue see: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6ecf830e5029598732e04067e325d946097519cb + // + // Note: this function is marked "naked" because we must guarantee that + // our NOPs occur before any compiler generated prologue. + template + static __attribute__((naked)) void SignalHandlerTrampoline(int signal, + siginfo_t* info, + void* context_) { + // IT (If-Then) instruction makes up to four instructions that follow it + // conditional. + asm volatile("nop; nop; nop; nop" : : : "memory"); + + // Tail-call into the actual signal handler. + // Note: this code is split into a separate inline assembly block because + // any code that compiler generates to satisfy register constraints must + // be generated after four NOPs. + register int arg0 asm("r0") = signal; + register siginfo_t* arg1 asm("r1") = info; + register void* arg2 asm("r2") = context_; + asm volatile("bx %3" + : + : "r"(arg0), "r"(arg1), "r"(arg2), + "r"(action) + : "memory"); + } +#endif // defined(USE_SIGNAL_HANDLER_TRAMPOLINE) }; +#undef USE_SIGNAL_HANDLER_TRAMPOLINE + + } // namespace dart #endif // VM_SIGNAL_HANDLER_H_ diff --git a/runtime/vm/signal_handler_android.cc b/runtime/vm/signal_handler_android.cc index d41eb594ad8..64670dea206 100644 --- a/runtime/vm/signal_handler_android.cc +++ b/runtime/vm/signal_handler_android.cc @@ -100,7 +100,7 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) { } -void SignalHandler::Install(SignalAction action) { +void SignalHandler::InstallImpl(SignalAction action) { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_sigaction = action; diff --git a/runtime/vm/signal_handler_linux.cc b/runtime/vm/signal_handler_linux.cc index b49f90a7678..1df46f0cb11 100644 --- a/runtime/vm/signal_handler_linux.cc +++ b/runtime/vm/signal_handler_linux.cc @@ -99,7 +99,7 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) { } -void SignalHandler::Install(SignalAction action) { +void SignalHandler::InstallImpl(SignalAction action) { struct sigaction act; act.sa_handler = NULL; act.sa_sigaction = action; diff --git a/runtime/vm/signal_handler_macos.cc b/runtime/vm/signal_handler_macos.cc index ca6e64d5784..bfcc42cdb05 100644 --- a/runtime/vm/signal_handler_macos.cc +++ b/runtime/vm/signal_handler_macos.cc @@ -102,7 +102,7 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) { } -void SignalHandler::Install(SignalAction action) { +void SignalHandler::InstallImpl(SignalAction action) { struct sigaction act; act.sa_handler = NULL; act.sa_sigaction = action; diff --git a/runtime/vm/signal_handler_win.cc b/runtime/vm/signal_handler_win.cc index e3164f45264..bb6b92d01fe 100644 --- a/runtime/vm/signal_handler_win.cc +++ b/runtime/vm/signal_handler_win.cc @@ -38,7 +38,7 @@ uintptr_t SignalHandler::GetLinkRegister(const mcontext_t& mcontext) { } -void SignalHandler::Install(SignalAction action) { +void SignalHandler::InstallImpl(SignalAction action) { UNIMPLEMENTED(); } diff --git a/runtime/vm/thread_interrupter_android.cc b/runtime/vm/thread_interrupter_android.cc index 82be148cbd0..58f302fecf4 100644 --- a/runtime/vm/thread_interrupter_android.cc +++ b/runtime/vm/thread_interrupter_android.cc @@ -57,8 +57,8 @@ void ThreadInterrupter::InterruptThread(OSThread* thread) { void ThreadInterrupter::InstallSignalHandler() { - SignalHandler::Install( - ThreadInterrupterAndroid::ThreadInterruptSignalHandler); + SignalHandler::Install< + ThreadInterrupterAndroid::ThreadInterruptSignalHandler>(); } diff --git a/runtime/vm/thread_interrupter_linux.cc b/runtime/vm/thread_interrupter_linux.cc index 28ec681fa55..4eb07f494b2 100644 --- a/runtime/vm/thread_interrupter_linux.cc +++ b/runtime/vm/thread_interrupter_linux.cc @@ -54,7 +54,8 @@ void ThreadInterrupter::InterruptThread(OSThread* thread) { void ThreadInterrupter::InstallSignalHandler() { - SignalHandler::Install(ThreadInterrupterLinux::ThreadInterruptSignalHandler); + SignalHandler::Install< + ThreadInterrupterLinux::ThreadInterruptSignalHandler>(); } diff --git a/runtime/vm/thread_interrupter_macos.cc b/runtime/vm/thread_interrupter_macos.cc index e46dbc7d2a7..222a294e72e 100644 --- a/runtime/vm/thread_interrupter_macos.cc +++ b/runtime/vm/thread_interrupter_macos.cc @@ -55,7 +55,8 @@ void ThreadInterrupter::InterruptThread(OSThread* thread) { void ThreadInterrupter::InstallSignalHandler() { - SignalHandler::Install(ThreadInterrupterMacOS::ThreadInterruptSignalHandler); + SignalHandler::Install< + ThreadInterrupterMacOS::ThreadInterruptSignalHandler>(); }