aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2018-03-14 15:21:37 -0700
committerKeith Randall <khr@golang.org>2018-03-15 17:31:57 +0000
commit9d4215311ba573a5b676de85b053eec03e577478 (patch)
tree439397eba561aa95ee38129a61b5afe735b1f2b4 /src/runtime
parentcd2cb6e3f57e4820d66dbefd7577048c38ee9e04 (diff)
downloadgo-9d4215311ba573a5b676de85b053eec03e577478.tar.xz
runtime: identify special functions by flag instead of address
When there are plugins, there may not be a unique copy of runtime functions like goexit, mcall, etc. So identifying them by entry address is problematic. Instead, keep track of each special function using a field in the symbol table. That way, multiple copies of the same runtime function will be treated identically. Fixes #24351 Fixes #23133 Change-Id: Iea3232df8a6af68509769d9ca618f530cc0f84fd Reviewed-on: https://go-review.googlesource.com/100739 Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/os_windows.go2
-rw-r--r--src/runtime/proc.go9
-rw-r--r--src/runtime/runtime2.go4
-rw-r--r--src/runtime/stack.go5
-rw-r--r--src/runtime/symtab.go33
-rw-r--r--src/runtime/traceback.go91
6 files changed, 66 insertions, 78 deletions
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index d27bf74455..415ec0cb90 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -304,8 +304,6 @@ func osinit() {
disableWER()
- externalthreadhandlerp = funcPC(externalthreadhandler)
-
initExceptionHandler()
stdcall2(_SetConsoleCtrlHandler, funcPC(ctrlhandler), 1)
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 008bd244e0..8f32320e0c 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -393,6 +393,11 @@ func releaseSudog(s *sudog) {
// funcPC returns the entry PC of the function f.
// It assumes that f is a func value. Otherwise the behavior is undefined.
+// CAREFUL: In programs with plugins, funcPC can return different values
+// for the same function (because there are actually multiple copies of
+// the same function in the address space). To be safe, don't use the
+// results of this function in any == expression. It is only safe to
+// use the result as an address at which to start executing code.
//go:nosplit
func funcPC(f interface{}) uintptr {
return **(**uintptr)(add(unsafe.Pointer(&f), sys.PtrSize))
@@ -3789,8 +3794,8 @@ func setsSP(pc uintptr) bool {
// so assume the worst and stop traceback
return true
}
- switch f.entry {
- case gogoPC, systemstackPC, mcallPC, morestackPC:
+ switch f.funcID {
+ case funcID_gogo, funcID_systemstack, funcID_mcall, funcID_morestack:
return true
}
return false
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index e6808ac023..059e14e002 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -638,8 +638,8 @@ type _func struct {
entry uintptr // start pc
nameoff int32 // function name
- args int32 // in/out args size
- _ int32 // previously legacy frame size; kept for layout compatibility
+ args int32 // in/out args size
+ funcID funcID // set for certain special runtime functions
pcsp int32
pcfile int32
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 8f10508a7d..2dc7001e38 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -619,7 +619,7 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool {
if stackDebug >= 2 {
print(" adjusting ", funcname(f), " frame=[", hex(frame.sp), ",", hex(frame.fp), "] pc=", hex(frame.pc), " continpc=", hex(frame.continpc), "\n")
}
- if f.entry == systemstack_switchPC {
+ if f.funcID == funcID_systemstack_switch {
// A special routine at the bottom of stack of a goroutine that does an systemstack call.
// We will allow it to be copied even though we don't
// have full GC info for it (because it is written in asm).
@@ -1110,7 +1110,8 @@ func shrinkstack(gp *g) {
if debug.gcshrinkstackoff > 0 {
return
}
- if gp.startpc == gcBgMarkWorkerPC {
+ f := findfunc(gp.startpc)
+ if f.valid() && f.funcID == funcID_gcBgMarkWorker {
// We're not allowed to shrink the gcBgMarkWorker
// stack (see gcBgMarkWorker for explanation).
return
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 7d7c363b5b..91a8f8eb05 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -133,7 +133,7 @@ again:
}
se.pcExpander.init(ncallers[0], se.wasPanic)
ncallers = ncallers[1:]
- se.wasPanic = se.pcExpander.funcInfo.valid() && se.pcExpander.funcInfo.entry == sigpanicPC
+ se.wasPanic = se.pcExpander.funcInfo.valid() && se.pcExpander.funcInfo.funcID == funcID_sigpanic
if se.skip > 0 {
for ; se.skip > 0; se.skip-- {
se.pcExpander.next()
@@ -349,6 +349,35 @@ const (
_ArgsSizeUnknown = -0x80000000
)
+// A FuncID identifies particular functions that need to be treated
+// specially by the runtime.
+// Note that in some situations involving plugins, there may be multiple
+// copies of a particular special runtime function.
+// Note: this list must match the list in cmd/internal/objabi/funcid.go.
+type funcID uint32
+
+const (
+ funcID_normal funcID = iota // not a special function
+ funcID_goexit
+ funcID_jmpdefer
+ funcID_mcall
+ funcID_morestack
+ funcID_mstart
+ funcID_rt0_go
+ funcID_asmcgocall
+ funcID_sigpanic
+ funcID_runfinq
+ funcID_bgsweep
+ funcID_forcegchelper
+ funcID_timerproc
+ funcID_gcBgMarkWorker
+ funcID_systemstack_switch
+ funcID_systemstack
+ funcID_cgocallback_gofunc
+ funcID_gogo
+ funcID_externalthreadhandler
+)
+
// moduledata records information about the layout of the executable
// image. It is written by the linker. Any changes here must be
// matched changes to the code in cmd/internal/ld/symtab.go:symtab.
@@ -648,7 +677,6 @@ func findfunc(pc uintptr) funcInfo {
idx = uint32(len(datap.ftab) - 1)
}
if pc < datap.ftab[idx].entry {
-
// With multiple text sections, the idx might reference a function address that
// is higher than the pc being searched, so search backward until the matching address is found.
@@ -659,7 +687,6 @@ func findfunc(pc uintptr) funcInfo {
throw("findfunc: bad findfunctab entry idx")
}
} else {
-
// linear search to find func with pc >= entry.
for datap.ftab[idx+1].entry <= pc {
idx++
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 2261942ab4..06df9385fd 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -35,56 +35,14 @@ import (
const usesLR = sys.MinFrameSize > 0
-var (
- // initialized in tracebackinit
- goexitPC uintptr
- jmpdeferPC uintptr
- mcallPC uintptr
- morestackPC uintptr
- mstartPC uintptr
- rt0_goPC uintptr
- asmcgocallPC uintptr
- sigpanicPC uintptr
- runfinqPC uintptr
- bgsweepPC uintptr
- forcegchelperPC uintptr
- timerprocPC uintptr
- gcBgMarkWorkerPC uintptr
- systemstack_switchPC uintptr
- systemstackPC uintptr
- cgocallback_gofuncPC uintptr
- skipPC uintptr
-
- gogoPC uintptr
-
- externalthreadhandlerp uintptr // initialized elsewhere
-)
+var skipPC uintptr
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.
- goexitPC = funcPC(goexit)
- jmpdeferPC = funcPC(jmpdefer)
- mcallPC = funcPC(mcall)
- morestackPC = funcPC(morestack)
- mstartPC = funcPC(mstart)
- rt0_goPC = funcPC(rt0_go)
- asmcgocallPC = funcPC(asmcgocall)
- sigpanicPC = funcPC(sigpanic)
- runfinqPC = funcPC(runfinq)
- bgsweepPC = funcPC(bgsweep)
- forcegchelperPC = funcPC(forcegchelper)
- timerprocPC = funcPC(timerproc)
- gcBgMarkWorkerPC = funcPC(gcBgMarkWorker)
- systemstack_switchPC = funcPC(systemstack_switch)
- systemstackPC = funcPC(systemstack)
- cgocallback_gofuncPC = funcPC(cgocallback_gofunc)
skipPC = funcPC(skipPleaseUseCallersFrames)
-
- // used by sigprof handler
- gogoPC = funcPC(gogo)
}
// Traceback over the deferred function calls.
@@ -137,9 +95,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
if skip > 0 && callback != nil {
throw("gentraceback callback cannot be used with non-zero skip")
}
- if goexitPC == 0 {
- throw("gentraceback before goexitPC initialization")
- }
g := getg()
if g == gp && g == g.m.curg {
// The starting sp has been passed in as a uintptr, and the caller may
@@ -241,7 +196,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// g0, this systemstack is at the top of the stack.
// if we're not on g0 or there's a no curg, then this is a regular call.
sp := frame.sp
- if flags&_TraceJumpStack != 0 && f.entry == systemstackPC && gp == g.m.g0 && gp.m.curg != nil {
+ if flags&_TraceJumpStack != 0 && f.funcID == funcID_systemstack && gp == g.m.g0 && gp.m.curg != nil {
sp = gp.m.curg.sched.sp
frame.sp = sp
cgoCtxt = gp.m.curg.cgoCtxt
@@ -256,7 +211,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
if topofstack(f, gp.m != nil && gp == gp.m.g0) {
frame.lr = 0
flr = funcInfo{}
- } else if usesLR && f.entry == jmpdeferPC {
+ } else if usesLR && f.funcID == funcID_jmpdefer {
// jmpdefer modifies SP/LR/PC non-atomically.
// If a profiling interrupt arrives during jmpdefer,
// the stack unwind may see a mismatched register set
@@ -287,7 +242,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// But if callback is set, we're doing a garbage collection and must
// get everything, so crash loudly.
doPrint := printing
- if doPrint && gp.m.incgo && f.entry == sigpanicPC {
+ if doPrint && gp.m.incgo && f.funcID == funcID_sigpanic {
// We can inject sigpanic
// calls directly into C code,
// in which case we'll see a C
@@ -396,8 +351,8 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// if there's room, pcbuf[1] is a skip PC that encodes the number of skipped frames in pcbuf[0]
if n+1 < max {
n++
- skipPC := funcPC(skipPleaseUseCallersFrames) + uintptr(logicalSkipped)
- (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = skipPC
+ pc := skipPC + uintptr(logicalSkipped)
+ (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = pc
}
}
}
@@ -466,7 +421,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
n++
skipped:
- if f.entry == cgocallback_gofuncPC && len(cgoCtxt) > 0 {
+ if f.funcID == funcID_cgocallback_gofunc && len(cgoCtxt) > 0 {
ctxt := cgoCtxt[len(cgoCtxt)-1]
cgoCtxt = cgoCtxt[:len(cgoCtxt)-1]
@@ -478,7 +433,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
}
}
- waspanic = f.entry == sigpanicPC
+ waspanic = f.funcID == funcID_sigpanic
// Do not unwind past the bottom of the stack.
if !flr.valid() {
@@ -931,30 +886,32 @@ func tracebackHexdump(stk stack, frame *stkframe, bad uintptr) {
// Does f mark the top of a goroutine stack?
func topofstack(f funcInfo, g0 bool) bool {
- pc := f.entry
- return pc == goexitPC ||
- pc == mstartPC ||
- pc == mcallPC ||
- pc == morestackPC ||
- pc == rt0_goPC ||
- externalthreadhandlerp != 0 && pc == externalthreadhandlerp ||
+ return f.funcID == funcID_goexit ||
+ f.funcID == funcID_mstart ||
+ f.funcID == funcID_mcall ||
+ f.funcID == funcID_morestack ||
+ f.funcID == funcID_rt0_go ||
+ f.funcID == funcID_externalthreadhandler ||
// asmcgocall is TOS on the system stack because it
// switches to the system stack, but in this case we
// can come back to the regular stack and still want
// to be able to unwind through the call that appeared
// on the regular stack.
- (g0 && pc == asmcgocallPC)
+ (g0 && f.funcID == funcID_asmcgocall)
}
// isSystemGoroutine reports whether the goroutine g must be omitted in
// stack dumps and deadlock detector.
func isSystemGoroutine(gp *g) bool {
- pc := gp.startpc
- return pc == runfinqPC && !fingRunning ||
- pc == bgsweepPC ||
- pc == forcegchelperPC ||
- pc == timerprocPC ||
- pc == gcBgMarkWorkerPC
+ f := findfunc(gp.startpc)
+ if !f.valid() {
+ return false
+ }
+ return f.funcID == funcID_runfinq && !fingRunning ||
+ f.funcID == funcID_bgsweep ||
+ f.funcID == funcID_forcegchelper ||
+ f.funcID == funcID_timerproc ||
+ f.funcID == funcID_gcBgMarkWorker
}
// SetCgoTraceback records three C functions to use to gather