When starting a system call, the libgo library needs to save the registers in a place where the garbage collector can see them, so that if the registers happen to be pointers the objects to which they point will not be collected. I thought that I could simply use setjmp for that. Unfortunately, I just managed to uncover the fact that setjmp mangles the register values, so that the garbage collector does not understand them. This means that a particularly poorly timed garbage collection can discard values used by a system call. This patch changes libgo to use getcontext as well, which should work at least until getcontext also gets pointer mangling. It appears that in the long run we will need processor-specific context save and restore routines in libgo. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline and 4.7 branch.
Ian
diff -r edb476bbcabd libgo/runtime/proc.c --- a/libgo/runtime/proc.c Fri May 18 13:14:19 2012 -0700 +++ b/libgo/runtime/proc.c Tue May 22 09:51:13 2012 -0700 @@ -1239,9 +1239,7 @@ // Save the registers in the g structure so that any pointers // held in registers will be seen by the garbage collector. - // We could use getcontext here, but setjmp is more efficient - // because it doesn't need to save the signal mask. - setjmp(g->gcregs); + getcontext(&g->gcregs); g->status = Gsyscall; @@ -1299,7 +1297,7 @@ gp->gcstack = nil; #endif gp->gcnext_sp = nil; - runtime_memclr(gp->gcregs, sizeof gp->gcregs); + runtime_memclr(&gp->gcregs, sizeof gp->gcregs); if(m->profilehz > 0) runtime_setprof(true); @@ -1328,7 +1326,7 @@ gp->gcstack = nil; #endif gp->gcnext_sp = nil; - runtime_memclr(gp->gcregs, sizeof gp->gcregs); + runtime_memclr(&gp->gcregs, sizeof gp->gcregs); } // Allocate a new g, with a stack big enough for stacksize bytes. diff -r edb476bbcabd libgo/runtime/runtime.h --- a/libgo/runtime/runtime.h Fri May 18 13:14:19 2012 -0700 +++ b/libgo/runtime/runtime.h Tue May 22 09:51:13 2012 -0700 @@ -7,7 +7,6 @@ #include "config.h" #include "go-assert.h" -#include <setjmp.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -128,7 +127,7 @@ void* gcnext_segment; void* gcnext_sp; void* gcinitial_sp; - jmp_buf gcregs; + ucontext_t gcregs; byte* entry; // initial function G* alllink; // on allg void* param; // passed parameter on wakeup