diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index db2ffb5752f..ae7d57566f7 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -7797,7 +7797,7 @@ func callTargetLSym(callee *ir.Name) *obj.LSym { } // deferStructFnField is the field index of _defer.fn. -const deferStructFnField = 3 +const deferStructFnField = 4 var deferType *types.Type @@ -7817,6 +7817,7 @@ func deferstruct() *types.Type { makefield("heap", types.Types[types.TBOOL]), makefield("rangefunc", types.Types[types.TBOOL]), makefield("sp", types.Types[types.TUINTPTR]), + makefield("pc", types.Types[types.TUINTPTR]), // Note: the types here don't really matter. Defer structures // are always scanned explicitly during stack copying and GC, // so we make them uintptr type even though they are real pointers. diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 3a8c374fc0f..3671c65ab73 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -382,6 +382,7 @@ func dumpgoroutine(gp *g) { dumpint(uint64(uintptr(unsafe.Pointer(d)))) dumpint(uint64(uintptr(unsafe.Pointer(gp)))) dumpint(uint64(d.sp)) + dumpint(uint64(d.pc)) fn := *(**funcval)(unsafe.Pointer(&d.fn)) dumpint(uint64(uintptr(unsafe.Pointer(fn)))) if d.fn == nil { diff --git a/src/runtime/panic.go b/src/runtime/panic.go index ded85feffa1..325bf74aa82 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -354,6 +354,7 @@ func deferproc(fn func()) { d.link = gp._defer gp._defer = d d.fn = fn + d.pc = sys.GetCallerPC() // We must not be preempted between calling GetCallerSP and // storing it to d.sp because GetCallerSP's result is a // uintptr stack pointer. @@ -457,6 +458,7 @@ func deferrangefunc() any { d := newdefer() d.link = gp._defer gp._defer = d + d.pc = sys.GetCallerPC() // We must not be preempted between calling GetCallerSP and // storing it to d.sp because GetCallerSP's result is a // uintptr stack pointer. @@ -516,6 +518,7 @@ func deferconvert(d0 *_defer) { } for d1 := d; ; d1 = d1.link { d1.sp = d0.sp + d1.pc = d0.pc if d1.link == nil { d1.link = tail break @@ -544,6 +547,7 @@ func deferprocStack(d *_defer) { d.heap = false d.rangefunc = false d.sp = sys.GetCallerSP() + d.pc = sys.GetCallerPC() // The lines below implement: // d.link = gp._defer // d.head = nil @@ -971,6 +975,8 @@ func (p *_panic) nextDefer() (func(), bool) { fn := d.fn + p.retpc = d.pc + // Unlink and free. popDefer(gp) @@ -1010,12 +1016,6 @@ func (p *_panic) nextFrame() (ok bool) { // it's non-zero. if u.frame.sp == limit { - f := u.frame.fn - if f.deferreturn == 0 { - throw("no deferreturn") - } - p.retpc = f.entry() + uintptr(f.deferreturn) - break // found a frame with linked defers } @@ -1271,6 +1271,15 @@ func recovery(gp *g) { pc, sp, fp := p.retpc, uintptr(p.sp), uintptr(p.fp) p0, saveOpenDeferState := p, p.deferBitsPtr != nil && *p.deferBitsPtr != 0 + // The linker records the f-relative address of a call to deferreturn in f's funcInfo. + // Assuming a "normal" call to recover() inside one of f's deferred functions + // invoked for a panic, that is the desired PC for exiting f. + f := findfunc(pc) + if f.deferreturn == 0 { + throw("no deferreturn") + } + gotoPc := f.entry() + uintptr(f.deferreturn) + // Unwind the panic stack. for ; p != nil && uintptr(p.startSP) < sp; p = p.link { // Don't allow jumping past a pending Goexit. @@ -1293,7 +1302,7 @@ func recovery(gp *g) { // With how subtle defer handling is, this might not actually be // worthwhile though. if p.goexit { - pc, sp = p.startPC, uintptr(p.startSP) + gotoPc, sp = p.startPC, uintptr(p.startSP) saveOpenDeferState = false // goexit is unwinding the stack anyway break } @@ -1356,7 +1365,7 @@ func recovery(gp *g) { // branch directly to the deferreturn gp.sched.sp = sp - gp.sched.pc = pc + gp.sched.pc = gotoPc gp.sched.lr = 0 // Restore the bp on platforms that support frame pointers. // N.B. It's fine to not set anything for platforms that don't diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 1deeb1244ca..6d1f9b13a28 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -1090,6 +1090,7 @@ type _defer struct { heap bool rangefunc bool // true for rangefunc list sp uintptr // sp at time of defer + pc uintptr // pc at time of defer fn func() // can be nil for open-coded defers link *_defer // next defer on G; can point to either heap or stack!