diff -r 5b77b481d6f9 libgo/runtime/proc.c
--- a/libgo/runtime/proc.c	Mon Feb 13 16:29:13 2012 -0800
+++ b/libgo/runtime/proc.c	Mon Feb 13 16:30:31 2012 -0800
@@ -309,6 +309,8 @@
 static void
 runtime_mcall(void (*pfn)(G*))
 {
+	M *mp;
+	G *gp;
 #ifndef USING_SPLIT_STACK
 	int i;
 #endif
@@ -317,28 +319,45 @@
 	// collector.
 	__builtin_unwind_init();
 
-	if(g == m->g0)
+	mp = m;
+	gp = g;
+	if(gp == mp->g0)
 		runtime_throw("runtime: mcall called on m->g0 stack");
 
-	if(g != nil) {
+	if(gp != nil) {
 
 #ifdef USING_SPLIT_STACK
 		__splitstack_getcontext(&g->stack_context[0]);
 #else
-		g->gcnext_sp = &i;
+		gp->gcnext_sp = &i;
 #endif
-		g->fromgogo = false;
-		getcontext(&g->context);
+		gp->fromgogo = false;
+		getcontext(&gp->context);
+
+		// When we return from getcontext, we may be running
+		// in a new thread.  That means that m and g may have
+		// changed.  They are global variables so we will
+		// reload them, but the addresses of m and g may be
+		// cached in our local stack frame, and those
+		// addresses may be wrong.  Call functions to reload
+		// the values for this thread.
+		mp = runtime_m();
+		gp = runtime_g();
 	}
-	if (g == nil || !g->fromgogo) {
+	if (gp == nil || !gp->fromgogo) {
 #ifdef USING_SPLIT_STACK
-		__splitstack_setcontext(&m->g0->stack_context[0]);
+		__splitstack_setcontext(&mp->g0->stack_context[0]);
 #endif
-		m->g0->entry = (byte*)pfn;
-		m->g0->param = g;
-		g = m->g0;
-		fixcontext(&m->g0->context);
-		setcontext(&m->g0->context);
+		mp->g0->entry = (byte*)pfn;
+		mp->g0->param = gp;
+
+		// It's OK to set g directly here because this case
+		// can not occur if we got here via a setcontext to
+		// the getcontext call just above.
+		g = mp->g0;
+
+		fixcontext(&mp->g0->context);
+		setcontext(&mp->g0->context);
 		runtime_throw("runtime: mcall function returned");
 	}
 }
