1
0
mirror of https://github.com/golang/go synced 2024-11-26 20:21:25 -07:00

runtime: fix x86 stack trace for call to heap memory

Fixes #11656.

Change-Id: Ib81d583e4b004e67dc9d2f898fd798112434e7a9
Reviewed-on: https://go-review.googlesource.com/12026
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
This commit is contained in:
Russ Cox 2015-07-10 12:32:03 -04:00
parent 683311175c
commit 0bcdffeea6
4 changed files with 107 additions and 22 deletions

View File

@ -67,21 +67,31 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
} }
} }
// Only push runtime.sigpanic if rip != 0. pc := uintptr(c.eip())
// If rip == 0, probably panicked because of a sp := uintptr(c.esp())
// If we don't recognize the PC as code
// but we do recognize the top pointer on the stack as code,
// then assume this was a call to non-code and treat like
// pc == 0, to make unwinding show the context.
if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
pc = 0
}
// Only push runtime.sigpanic if pc != 0.
// If pc == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will // call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime.sigpanic instead. // make the trace look like a call to runtime.sigpanic instead.
// (Otherwise the trace will end at runtime.sigpanic and we // (Otherwise the trace will end at runtime.sigpanic and we
// won't get to see who faulted.) // won't get to see who faulted.)
if c.eip() != 0 { if pc != 0 {
sp := c.esp()
if regSize > ptrSize { if regSize > ptrSize {
sp -= ptrSize sp -= ptrSize
*(*uintptr)(unsafe.Pointer(uintptr(sp))) = 0 *(*uintptr)(unsafe.Pointer(sp)) = 0
} }
sp -= ptrSize sp -= ptrSize
*(*uintptr)(unsafe.Pointer(uintptr(sp))) = uintptr(c.eip()) *(*uintptr)(unsafe.Pointer(sp)) = pc
c.set_esp(sp) c.set_esp(uint32(sp))
} }
c.set_eip(uint32(funcPC(sigpanic))) c.set_eip(uint32(funcPC(sigpanic)))
return return

View File

@ -101,21 +101,31 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
} }
} }
// Only push runtime.sigpanic if rip != 0. pc := uintptr(c.rip())
// If rip == 0, probably panicked because of a sp := uintptr(c.rsp())
// If we don't recognize the PC as code
// but we do recognize the top pointer on the stack as code,
// then assume this was a call to non-code and treat like
// pc == 0, to make unwinding show the context.
if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
pc = 0
}
// Only push runtime.sigpanic if pc != 0.
// If pc == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will // call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime.sigpanic instead. // make the trace look like a call to runtime.sigpanic instead.
// (Otherwise the trace will end at runtime.sigpanic and we // (Otherwise the trace will end at runtime.sigpanic and we
// won't get to see who faulted.) // won't get to see who faulted.)
if c.rip() != 0 { if pc != 0 {
sp := c.rsp()
if regSize > ptrSize { if regSize > ptrSize {
sp -= ptrSize sp -= ptrSize
*(*uintptr)(unsafe.Pointer(uintptr(sp))) = 0 *(*uintptr)(unsafe.Pointer(sp)) = 0
} }
sp -= ptrSize sp -= ptrSize
*(*uintptr)(unsafe.Pointer(uintptr(sp))) = uintptr(c.rip()) *(*uintptr)(unsafe.Pointer(sp)) = pc
c.set_rsp(sp) c.set_rsp(uint64(sp))
} }
c.set_rip(uint64(funcPC(sigpanic))) c.set_rip(uint64(funcPC(sigpanic)))
return return

View File

@ -0,0 +1,58 @@
// run
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// darwin/386 seems to mangle the PC and SP before
// it manages to invoke the signal handler, so this test fails there.
// +build !darwin !386
//
// openbsd/386 and plan9/386 don't work, not sure why.
// +build !openbsd !386
// +build !plan9 !386
//
// windows doesn't work, because Windows exception handling
// delivers signals based on the current PC, and that current PC
// doesn't go into the Go runtime.
// +build !windows
package main
import (
"runtime"
"runtime/debug"
"unsafe"
)
func main() {
debug.SetPanicOnFault(true)
defer func() {
if err := recover(); err == nil {
panic("not panicking")
}
pc, _, _, _ := runtime.Caller(10)
f := runtime.FuncForPC(pc)
if f == nil || f.Name() != "main.f" {
if f == nil {
println("no func for ", unsafe.Pointer(pc))
} else {
println("found func:", f.Name())
}
panic("cannot find main.f on stack")
}
}()
f(20)
}
func f(n int) {
if n > 0 {
f(n-1)
}
var f struct {
x uintptr
}
f.x = uintptr(unsafe.Pointer(&f))
fn := *(*func())(unsafe.Pointer(&f))
fn()
}

View File

@ -412,19 +412,13 @@ func (t *test) run() {
t.err = skipError("starts with newline") t.err = skipError("starts with newline")
return return
} }
// Execution recipe stops at first blank line.
pos := strings.Index(t.src, "\n\n") pos := strings.Index(t.src, "\n\n")
if pos == -1 { if pos == -1 {
t.err = errors.New("double newline not found") t.err = errors.New("double newline not found")
return return
} }
// Check for build constraints only upto the first blank line.
if ok, why := shouldTest(t.src[:pos], goos, goarch); !ok {
t.action = "skip"
if *showSkips {
fmt.Printf("%-20s %-20s: %s\n", t.action, t.goFileName(), why)
}
return
}
action := t.src[:pos] action := t.src[:pos]
if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") { if nl := strings.Index(action, "\n"); nl >= 0 && strings.Contains(action[:nl], "+build") {
// skip first line // skip first line
@ -434,6 +428,19 @@ func (t *test) run() {
action = action[2:] action = action[2:]
} }
// Check for build constraints only up to the actual code.
pkgPos := strings.Index(t.src, "\npackage")
if pkgPos == -1 {
pkgPos = pos // some files are intentionally malformed
}
if ok, why := shouldTest(t.src[:pkgPos], goos, goarch); !ok {
t.action = "skip"
if *showSkips {
fmt.Printf("%-20s %-20s: %s\n", t.action, t.goFileName(), why)
}
return
}
var args, flags []string var args, flags []string
wantError := false wantError := false
f := strings.Fields(action) f := strings.Fields(action)