runtime: avoid write barriers with traceback info

Unlike the gc runtime, libgo stores traceback information in location
structs, which contain strings.  Therefore, copying location structs
around appears to require write barriers, although in fact write
barriers are never important because the strings are never allocated
in Go memory.  They come from libbacktrace.

Some of the generated write barriers come at times when write barriers
are not permitted.  For example, exitsyscall, marked
nowritebarrierrec, calls exitsyscallfast which calls traceGoSysExit
which calls traceEvent which calls traceStackID which calls
trace.stackTab.put which copies location values into memory allocated
by tab.newStack.  This write barrier can be invoked when there is no
p, causing a crash.

This change fixes the problem by ensuring that location values are
copied around in the tracing code with no write barriers.

This was found by fixing the compiler to fully implement
//go:nowritebarrierrec; CL to follow.

Change-Id: Ia672fadb37c3334fdcb2af77cec2ffbbe813678a
Reviewed-on: https://go-review.googlesource.com/134226
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go
index 74325e3..12d7071 100644
--- a/libgo/go/runtime/proc.go
+++ b/libgo/go/runtime/proc.go
@@ -1140,7 +1140,7 @@
 func kickoff() {
 	gp := getg()
 
-	if gp.traceback != nil {
+	if gp.traceback != 0 {
 		gtraceback(gp)
 	}
 
@@ -3097,7 +3097,7 @@
 	} else {
 		resetNewG(newg, &sp, &spsize)
 	}
-	newg.traceback = nil
+	newg.traceback = 0
 
 	if readgstatus(newg) != _Gdead {
 		throw("newproc1: new g is not Gdead")
diff --git a/libgo/go/runtime/runtime2.go b/libgo/go/runtime/runtime2.go
index 0299d5a..2de1cc8 100644
--- a/libgo/go/runtime/runtime2.go
+++ b/libgo/go/runtime/runtime2.go
@@ -431,7 +431,7 @@
 
 	isSystemGoroutine bool // whether goroutine is a "system" goroutine
 
-	traceback *tracebackg // stack traceback buffer
+	traceback uintptr // stack traceback buffer
 
 	context      g_ucontext_t // saved context for setcontext
 	stackcontext [10]uintptr  // split-stack context
diff --git a/libgo/go/runtime/trace.go b/libgo/go/runtime/trace.go
index 8427e76..e2bbb5d 100644
--- a/libgo/go/runtime/trace.go
+++ b/libgo/go/runtime/trace.go
@@ -135,6 +135,7 @@
 }
 
 // traceBufHeader is per-P tracing buffer.
+//go:notinheap
 type traceBufHeader struct {
 	link      traceBufPtr              // in trace.empty/full
 	lastTicks uint64                   // when we wrote the last event
@@ -747,7 +748,8 @@
 	stk.n = len(pcs)
 	stkpc := stk.stack()
 	for i, pc := range pcs {
-		stkpc[i] = pc
+		// Use memmove to avoid write barrier.
+		memmove(unsafe.Pointer(&stkpc[i]), unsafe.Pointer(&pc), unsafe.Sizeof(pc))
 	}
 	part := int(hash % uintptr(len(tab.tab)))
 	stk.link = tab.tab[part]
diff --git a/libgo/go/runtime/traceback_gccgo.go b/libgo/go/runtime/traceback_gccgo.go
index 8551ec1..9456b1f 100644
--- a/libgo/go/runtime/traceback_gccgo.go
+++ b/libgo/go/runtime/traceback_gccgo.go
@@ -186,7 +186,7 @@
 	if gp != nil && gp != me {
 		print("\n")
 		goroutineheader(gp)
-		gp.traceback = (*tracebackg)(noescape(unsafe.Pointer(&tb)))
+		gp.traceback = (uintptr)(noescape(unsafe.Pointer(&tb)))
 		getTraceback(me, gp)
 		printtrace(tb.locbuf[:tb.c], nil)
 		printcreatedby(gp)
@@ -220,7 +220,7 @@
 			print("\tgoroutine in C code; stack unavailable\n")
 			printcreatedby(gp)
 		} else {
-			gp.traceback = (*tracebackg)(noescape(unsafe.Pointer(&tb)))
+			gp.traceback = (uintptr)(noescape(unsafe.Pointer(&tb)))
 			getTraceback(me, gp)
 			printtrace(tb.locbuf[:tb.c], nil)
 			printcreatedby(gp)
diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c
index d8d231b..7bd95a0 100644
--- a/libgo/runtime/proc.c
+++ b/libgo/runtime/proc.c
@@ -338,7 +338,7 @@
 		gp = runtime_g();
 		mp = gp->m;
 
-		if(gp->traceback != nil)
+		if(gp->traceback != 0)
 			gtraceback(gp);
 	}
 	if (gp == nil || !gp->fromgogo) {
@@ -443,7 +443,7 @@
 #endif
 	getcontext(ucontext_arg(&me->context[0]));
 
-	if (gp->traceback != nil) {
+	if (gp->traceback != 0) {
 		runtime_gogo(gp);
 	}
 }
@@ -457,8 +457,8 @@
 	Traceback* traceback;
 	M* holdm;
 
-	traceback = gp->traceback;
-	gp->traceback = nil;
+	traceback = (Traceback*)gp->traceback;
+	gp->traceback = 0;
 	holdm = gp->m;
 	if(holdm != nil && holdm != g->m)
 		runtime_throw("gtraceback: m is not nil");
@@ -510,7 +510,7 @@
 	// multiple times via the setcontext call in mcall.
 	getcontext(ucontext_arg(&gp->context[0]));
 
-	if(gp->traceback != nil) {
+	if(gp->traceback != 0) {
 		// Got here from getTraceback.
 		// I'm not sure this ever actually happens--getTraceback
 		// may always go to the getcontext call in mcall.