diff --git a/arch/x64/tls-switch.hh b/arch/x64/tls-switch.hh index 8be619dbef..651b7d0cde 100644 --- a/arch/x64/tls-switch.hh +++ b/arch/x64/tls-switch.hh @@ -48,6 +48,28 @@ public: } } }; +// +//Simple RAII utility classes that implement the logic to switch +//fsbase to the specified app address and back to the kernel one +class user_tls_switch { + thread_control_block *_kernel_tcb; +public: + user_tls_switch() { + asm volatile ( "movq %%gs:16, %0\n\t" : "=r"(_kernel_tcb)); + + //Switch to app tcb if app tcb present + if (_kernel_tcb->app_tcb) { + set_fsbase(reinterpret_cast(_kernel_tcb->app_tcb)); + } + } + + ~user_tls_switch() { + //Switch to kernel tcb if app tcb present + if (_kernel_tcb->app_tcb) { + set_fsbase(reinterpret_cast(_kernel_tcb->self)); + } + } +}; } diff --git a/core/sched.cc b/core/sched.cc index bdffe0bea9..61c388bf62 100644 --- a/core/sched.cc +++ b/core/sched.cc @@ -2088,6 +2088,16 @@ void with_thread_by_id(unsigned id, std::function f) { } } +thread *find_first_app_thread(std::function f) { + WITH_LOCK(thread_map_mutex) { + for (auto th : thread_map) { + if(th.second->is_app() && f(*th.second)) { + return th.second; + } + } + } + return nullptr; +} } diff --git a/include/osv/sched.hh b/include/osv/sched.hh index d2c3b9352a..b495f43ae8 100644 --- a/include/osv/sched.hh +++ b/include/osv/sched.hh @@ -1552,6 +1552,8 @@ void with_all_threads(std::function); // should return quickly. void with_thread_by_id(unsigned id, std::function); +thread *find_first_app_thread(std::function f); + } #endif /* SCHED_HH_ */ diff --git a/libc/signal.cc b/libc/signal.cc index a67df458c4..13946bd14b 100644 --- a/libc/signal.cc +++ b/libc/signal.cc @@ -21,6 +21,10 @@ #include #include +#ifdef __x86_64__ +#include "tls-switch.hh" +#endif + using namespace osv::clock::literals; namespace osv { @@ -393,6 +397,11 @@ int kill(pid_t pid, int sig) signal_actions[sigidx].sa_flags = 0; signal_actions[sigidx].sa_handler = SIG_DFL; } +#ifdef __x86_64__ + //In case this signal handler thread has specified app thread local storage + //let us switch to it before invoking the user handler routine + arch::user_tls_switch tls_switch; +#endif if (sa.sa_flags & SA_SIGINFO) { // FIXME: proper second (siginfo) and third (context) arguments (See example in call_signal_handler) sa.sa_sigaction(sig, nullptr, nullptr); @@ -401,6 +410,26 @@ int kill(pid_t pid, int sig) } }, sched::thread::attr().detached().stack(65536).name("signal_handler"), false, true); + //If we are running statically linked executable or a dynamic one with Linux + //dynamic linker, its threads very likely use app thread local storage and signal + //routine may rely on it presence. For that reason we use app TLS of the current + //thread if it has one. Otherwise we find 1st app thread with non-null app TLS + //and assign the signal handler thread app TLS to it so it is switched to (see above). + //TODO: Ideally we should only run the logic below if the current app is statically + //linked executable or a dynamic one ran with Linux dynamic linker + //(what if we are handling "Ctrl-C"?) + u64 app_tcb = sched::thread::current()->get_app_tcb(); + if (!app_tcb) { + auto first_app_thread = sched::find_first_app_thread([&](sched::thread &t) { + return t.get_app_tcb(); + }); + if (first_app_thread) { + app_tcb = first_app_thread->get_app_tcb(); + } + } + if (app_tcb) { + t->set_app_tcb(app_tcb); + } t->start(); } return 0;