pkg/proc: improve support unwinding from sigpanic (#3559)
See: https://github.com/golang/go/issues/63862 Fixes #3545
This commit is contained in:
parent
438d036fed
commit
d186e14fd9
6
_fixtures/nilptr.go
Normal file
6
_fixtures/nilptr.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var p_value *int = nil
|
||||||
|
*p_value = 1
|
||||||
|
}
|
||||||
@ -112,7 +112,7 @@ func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
|
|||||||
fctxt.CFA = rule
|
fctxt.CFA = rule
|
||||||
}
|
}
|
||||||
|
|
||||||
// We assume that RBP is the frame pointer and we want to keep it updated,
|
// We assume that RBP is the frame pointer, and we want to keep it updated,
|
||||||
// so that we can use it to unwind the stack even when we encounter frames
|
// so that we can use it to unwind the stack even when we encounter frames
|
||||||
// without descriptor entries.
|
// without descriptor entries.
|
||||||
// If there isn't a rule already we emit one.
|
// If there isn't a rule already we emit one.
|
||||||
|
|||||||
@ -117,7 +117,7 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
|
|||||||
fctxt.CFA = rule
|
fctxt.CFA = rule
|
||||||
}
|
}
|
||||||
|
|
||||||
// We assume that RBP is the frame pointer and we want to keep it updated,
|
// We assume that RBP is the frame pointer, and we want to keep it updated,
|
||||||
// so that we can use it to unwind the stack even when we encounter frames
|
// so that we can use it to unwind the stack even when we encounter frames
|
||||||
// without descriptor entries.
|
// without descriptor entries.
|
||||||
// If there isn't a rule already we emit one.
|
// If there isn't a rule already we emit one.
|
||||||
|
|||||||
@ -6358,3 +6358,27 @@ func TestNextGenericMethodThroughInterface(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue3545(t *testing.T) {
|
||||||
|
protest.AllowRecording(t)
|
||||||
|
withTestProcessArgs("nilptr", t, "", []string{}, protest.EnableOptimization, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||||
|
err := grp.Continue()
|
||||||
|
if err != nil && err.Error() == "bad access" {
|
||||||
|
grp.Continue()
|
||||||
|
}
|
||||||
|
locations, err := proc.ThreadStacktrace(p, p.CurrentThread(), 40)
|
||||||
|
assertNoError(err, t, "Stacktrace()")
|
||||||
|
var foundMain bool
|
||||||
|
for _, loc := range locations {
|
||||||
|
if loc.Call.Fn != nil && loc.Call.Fn.Name == "main.main" {
|
||||||
|
if foundMain {
|
||||||
|
t.Fatal("main.main found more than once in the stacktrace")
|
||||||
|
}
|
||||||
|
foundMain = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundMain {
|
||||||
|
t.Fatal("did not find main.main in stack trace")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
@ -481,7 +483,6 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
|
|||||||
for i, regRule := range framectx.Regs {
|
for i, regRule := range framectx.Regs {
|
||||||
if logflags.Stack() {
|
if logflags.Stack() {
|
||||||
logger.Debugf("\t%s rule %s ", it.bi.Arch.RegnumToString(i), ruleString(®Rule, it.bi.Arch.RegnumToString))
|
logger.Debugf("\t%s rule %s ", it.bi.Arch.RegnumToString(i), ruleString(®Rule, it.bi.Arch.RegnumToString))
|
||||||
|
|
||||||
}
|
}
|
||||||
reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
|
reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
|
||||||
if reg != nil {
|
if reg != nil {
|
||||||
@ -499,6 +500,22 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
|
|||||||
it.err = err
|
it.err = err
|
||||||
} else {
|
} else {
|
||||||
ret = reg.Uint64Val
|
ret = reg.Uint64Val
|
||||||
|
// On systems which use a link register to store the return address of a function,
|
||||||
|
// certain leaf functions may not have correct DWARF information present in the
|
||||||
|
// .debug_frame FDE when unwinding after a fatal signal. This is due to the fact
|
||||||
|
// that runtime.sigpanic inserts a frame to make it look like the function which
|
||||||
|
// triggered the signal called runtime.sigpanic directly, making the value of the
|
||||||
|
// link register unreliable. Instead, treat it as a non-leaf function and read the
|
||||||
|
// return address from the stack. For more details, see:
|
||||||
|
// https://github.com/golang/go/issues/63862#issuecomment-1802672629.
|
||||||
|
if it.frame.Call.Fn != nil && it.frame.Call.Fn.Name == "runtime.sigpanic" && it.bi.Arch.usesLR {
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
_, err := it.mem.ReadMemory(buf, uint64(it.regs.CFA))
|
||||||
|
if err != nil {
|
||||||
|
it.err = err
|
||||||
|
}
|
||||||
|
binary.Read(bytes.NewReader(buf), binary.LittleEndian, &ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
retaddr = uint64(it.regs.CFA + regRule.Offset)
|
retaddr = uint64(it.regs.CFA + regRule.Offset)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user