runtime: initialize traceback variables earlier

Our traceback code needs to know the PC of several special
functions, including goexit, mcall, etc.  Make sure that
these PCs are initialized before any traceback occurs.

Fixes #8766

LGTM=rsc
R=golang-codereviews, rsc, khr, bradfitz
CC=golang-codereviews
https://golang.org/cl/145570043
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index a93c421..24dc3ee 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -31,20 +31,36 @@
 const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386"
 
 var (
-	deferprocPC = funcPC(deferproc)
-	goexitPC    = funcPC(goexit)
-	jmpdeferPC  = funcPC(jmpdefer)
-	mcallPC     = funcPC(mcall)
-	morestackPC = funcPC(morestack)
-	mstartPC    = funcPC(mstart)
-	newprocPC   = funcPC(newproc)
-	newstackPC  = funcPC(newstack)
-	rt0_goPC    = funcPC(rt0_go)
-	sigpanicPC  = funcPC(sigpanic)
+	// initialized in tracebackinit
+	deferprocPC uintptr
+	goexitPC    uintptr
+	jmpdeferPC  uintptr
+	mcallPC     uintptr
+	morestackPC uintptr
+	mstartPC    uintptr
+	newprocPC   uintptr
+	rt0_goPC    uintptr
+	sigpanicPC  uintptr
 
 	externalthreadhandlerp uintptr // initialized elsewhere
 )
 
+func tracebackinit() {
+	// Go variable initialization happens late during runtime startup.
+	// Instead of initializing the variables above in the declarations,
+	// schedinit calls this function so that the variables are
+	// initialized and available earlier in the startup sequence.
+	deferprocPC = funcPC(deferproc)
+	goexitPC = funcPC(goexit)
+	jmpdeferPC = funcPC(jmpdefer)
+	mcallPC = funcPC(mcall)
+	morestackPC = funcPC(morestack)
+	mstartPC = funcPC(mstart)
+	newprocPC = funcPC(newproc)
+	rt0_goPC = funcPC(rt0_go)
+	sigpanicPC = funcPC(sigpanic)
+}
+
 // Traceback over the deferred function calls.
 // Report them like calls that have been invoked but not started executing yet.
 func tracebackdefers(gp *g, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer) {
@@ -81,6 +97,9 @@
 // collector (callback != nil).  A little clunky to merge these, but avoids
 // duplicating the code and all its subtlety.
 func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, printall bool) int {
+	if goexitPC == 0 {
+		gothrow("gentraceback before goexitPC initialization")
+	}
 	g := getg()
 	gotraceback := gotraceback(nil)
 	if pc0 == ^uintptr(0) && sp0 == ^uintptr(0) { // Signal to fetch saved values from gp.