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:
parent
683311175c
commit
0bcdffeea6
@ -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
|
||||||
|
@ -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
|
||||||
|
58
test/fixedbugs/issue11656.go
Normal file
58
test/fixedbugs/issue11656.go
Normal 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()
|
||||||
|
}
|
23
test/run.go
23
test/run.go
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user