@@ -549,6 +549,87 @@
STRUCT_EPOLL_EVENT_FD_OFFSET=${libgo_cv_c_epoll_event_fd_offset}
AC_SUBST(STRUCT_EPOLL_EVENT_FD_OFFSET)
+dnl See whether setcontext changes the value of TLS variables.
+AC_CACHE_CHECK([whether setcontext clobbers TLS variables],
+[libgo_cv_lib_setcontext_clobbers_tls],
+[LIBS_hold="$LIBS"
+LIBS="$LIBS $PTHREAD_LIBS"
+AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([
+#include <pthread.h>
+#include <stdlib.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+__thread int tls;
+
+static char stack[[10 * 1024 * 1024]];
+static ucontext_t c;
+
+/* Called via makecontext/setcontext. */
+
+static void
+cfn (void)
+{
+ exit (tls);
+}
+
+/* Called via pthread_create. */
+
+static void *
+tfn (void *dummy)
+{
+ /* The thread should still see this value after calling
+ setcontext. */
+ tls = 0;
+
+ setcontext (&c);
+
+ /* The call to setcontext should not return. */
+ abort ();
+}
+
+int
+main ()
+{
+ pthread_t tid;
+
+ /* The thread should not see this value. */
+ tls = 1;
+
+ if (getcontext (&c) < 0)
+ abort ();
+
+ c.uc_stack.ss_sp = stack;
+ c.uc_stack.ss_flags = 0;
+ c.uc_stack.ss_size = sizeof stack;
+ c.uc_link = NULL;
+ makecontext (&c, cfn, 0);
+
+ if (pthread_create (&tid, NULL, tfn, NULL) != 0)
+ abort ();
+
+ if (pthread_join (tid, NULL) != 0)
+ abort ();
+
+ /* The thread should have called exit. */
+ abort ();
+}
+])],
+[libgo_cv_lib_setcontext_clobbers_tls=no],
+[libgo_cv_lib_setcontext_clobbers_tls=yes],
+[case "$target" in
+ x86_64*-*-solaris2.10) libgo_cv_lib_setcontext_clobbers_tls=yes ;;
+ *) libgo_cv_lib_setcontext_clobbers_tls=no ;;
+ esac
+])
+LIBS="$LIBS_hold"
+])
+if test "$libgo_cv_lib_setcontext_clobbers_tls" = "yes"; then
+ AC_DEFINE(SETCONTEXT_CLOBBERS_TLS, 1,
+ [Define if setcontext clobbers TLS variables])
+fi
+
AC_CACHE_SAVE
if test ${multilib} = yes; then
@@ -60,6 +60,54 @@
static __thread G *g;
static __thread M *m;
+#ifndef SETCONTEXT_CLOBBERS_TLS
+
+static inline void
+initcontext(void)
+{
+}
+
+static inline void
+fixcontext(ucontext_t *c __attribute__ ((unused)))
+{
+}
+
+# else
+
+# if defined(__x86_64__) && defined(__sun__)
+
+// x86_64 Solaris 10 and 11 have a bug: setcontext switches the %fs
+// register to that of the thread which called getcontext. The effect
+// is that the address of all __thread variables changes. This bug
+// also affects pthread_self() and pthread_getspecific. We work
+// around it by clobbering the context field directly to keep %fs the
+// same.
+
+static __thread greg_t fs;
+
+static inline void
+initcontext(void)
+{
+ ucontext_t c;
+
+ getcontext(&c);
+ fs = c.uc_mcontext.gregs[REG_FSBASE];
+}
+
+static inline void
+fixcontext(ucontext_t* c)
+{
+ c->uc_mcontext.gregs[REG_FSBASE] = fs;
+}
+
+# else
+
+# error unknown case for SETCONTEXT_CLOBBERS_TLS
+
+# endif
+
+#endif
+
// We can not always refer to the TLS variables directly. The
// compiler will call tls_get_addr to get the address of the variable,
// and it may hold it in a register across a call to schedule. When
@@ -248,7 +296,9 @@
#endif
g = newg;
newg->fromgogo = true;
+ fixcontext(&newg->context);
setcontext(&newg->context);
+ runtime_throw("gogo setcontext returned");
}
// Save context and call fn passing g as a parameter. This is like
@@ -287,6 +337,7 @@
m->g0->entry = (byte*)pfn;
m->g0->param = g;
g = m->g0;
+ fixcontext(&m->g0->context);
setcontext(&m->g0->context);
runtime_throw("runtime: mcall function returned");
}
@@ -312,6 +363,8 @@
m->curg = g;
g->m = m;
+ initcontext();
+
m->nomemprof++;
runtime_mallocinit();
mcommoninit(m);
@@ -844,6 +897,8 @@
m = (M*)mp;
g = m->g0;
+ initcontext();
+
g->entry = nil;
g->param = nil;