// Copyright 2014 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. package runtime import ( "runtime/internal/atomic" "unsafe" ) // For gccgo, use go:linkname to export compiler-called functions. // //go:linkname deferproc //go:linkname deferprocStack //go:linkname deferreturn //go:linkname setdeferretaddr //go:linkname checkdefer //go:linkname gopanic //go:linkname canrecover //go:linkname makefuncfficanrecover //go:linkname makefuncreturning //go:linkname gorecover //go:linkname deferredrecover //go:linkname goPanicIndex //go:linkname goPanicIndexU //go:linkname goPanicSliceAlen //go:linkname goPanicSliceAlenU //go:linkname goPanicSliceAcap //go:linkname goPanicSliceAcapU //go:linkname goPanicSliceB //go:linkname goPanicSliceBU //go:linkname goPanicSlice3Alen //go:linkname goPanicSlice3AlenU //go:linkname goPanicSlice3Acap //go:linkname goPanicSlice3AcapU //go:linkname goPanicSlice3B //go:linkname goPanicSlice3BU //go:linkname goPanicSlice3C //go:linkname goPanicSlice3CU //go:linkname panicshift //go:linkname panicdivide //go:linkname panicmem // Temporary for C code to call: //go:linkname throw // Check to make sure we can really generate a panic. If the panic // was generated from the runtime, or from inside malloc, then convert // to a throw of msg. // pc should be the program counter of the compiler-generated code that // triggered this panic. func panicCheck1(pc uintptr, msg string) { name, _, _, _ := funcfileline(pc-1, -1, false) if hasPrefix(name, "runtime.") { throw(msg) } // TODO: is this redundant? How could we be in malloc // but not in the runtime? runtime/internal/*, maybe? gp := getg() if gp != nil && gp.m != nil && gp.m.mallocing != 0 { throw(msg) } } // Same as above, but calling from the runtime is allowed. // // Using this function is necessary for any panic that may be // generated by runtime.sigpanic, since those are always called by the // runtime. func panicCheck2(err string) { // panic allocates, so to avoid recursive malloc, turn panics // during malloc into throws. gp := getg() if gp != nil && gp.m != nil && gp.m.mallocing != 0 { throw(err) } } // Many of the following panic entry-points turn into throws when they // happen in various runtime contexts. These should never happen in // the runtime, and if they do, they indicate a serious issue and // should not be caught by user code. // // The panic{Index,Slice,divide,shift} functions are called by // code generated by the compiler for out of bounds index expressions, // out of bounds slice expressions, division by zero, and shift by negative. // The panicdivide (again), panicoverflow, panicfloat, and panicmem // functions are called by the signal handler when a signal occurs // indicating the respective problem. // // Since panic{Index,Slice,shift} are never called directly, and // since the runtime package should never have an out of bounds slice // or array reference or negative shift, if we see those functions called from the // runtime package we turn the panic into a throw. That will dump the // entire runtime stack for easier debugging. // // The entry points called by the signal handler will be called from // runtime.sigpanic, so we can't disallow calls from the runtime to // these (they always look like they're called from the runtime). // Hence, for these, we just check for clearly bad runtime conditions. // failures in the comparisons for s[x], 0 <= x < y (y == len(s)) func goPanicIndex(x int, y int) { panicCheck1(getcallerpc(), "index out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsIndex}) } func goPanicIndexU(x uint, y int) { panicCheck1(getcallerpc(), "index out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex}) } // failures in the comparisons for s[:x], 0 <= x <= y (y == len(s) or cap(s)) func goPanicSliceAlen(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAlen}) } func goPanicSliceAlenU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen}) } func goPanicSliceAcap(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAcap}) } func goPanicSliceAcapU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap}) } // failures in the comparisons for s[x:y], 0 <= x <= y func goPanicSliceB(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceB}) } func goPanicSliceBU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB}) } // failures in the comparisons for s[::x], 0 <= x <= y (y == len(s) or cap(s)) func goPanicSlice3Alen(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Alen}) } func goPanicSlice3AlenU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Alen}) } func goPanicSlice3Acap(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Acap}) } func goPanicSlice3AcapU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Acap}) } // failures in the comparisons for s[:x:y], 0 <= x <= y func goPanicSlice3B(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3B}) } func goPanicSlice3BU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3B}) } // failures in the comparisons for s[x:y:], 0 <= x <= y func goPanicSlice3C(x int, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3C}) } func goPanicSlice3CU(x uint, y int) { panicCheck1(getcallerpc(), "slice bounds out of range") panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C}) } var shiftError = error(errorString("negative shift amount")) func panicshift() { panicCheck1(getcallerpc(), "negative shift amount") panic(shiftError) } var divideError = error(errorString("integer divide by zero")) func panicdivide() { panicCheck2("integer divide by zero") panic(divideError) } var overflowError = error(errorString("integer overflow")) func panicoverflow() { panicCheck2("integer overflow") panic(overflowError) } var floatError = error(errorString("floating point error")) func panicfloat() { panicCheck2("floating point error") panic(floatError) } var memoryError = error(errorString("invalid memory address or nil pointer dereference")) func panicmem() { panicCheck2("invalid memory address or nil pointer dereference") panic(memoryError) } func panicmemAddr(addr uintptr) { panicCheck2("invalid memory address or nil pointer dereference") panic(errorAddressString{msg: "invalid memory address or nil pointer dereference", addr: addr}) } // deferproc creates a new deferred function. // The compiler turns a defer statement into a call to this. // frame points into the stack frame; it is used to determine which // deferred functions are for the current stack frame, and whether we // have already deferred functions for this frame. // pfn is a C function pointer. // arg is a value to pass to pfn. func deferproc(frame *bool, pfn uintptr, arg unsafe.Pointer) { gp := getg() d := newdefer() if d._panic != nil { throw("deferproc: d.panic != nil after newdefer") } d.link = gp._defer gp._defer = d d.frame = frame d.panicStack = getg()._panic d.pfn = pfn d.arg = arg d.retaddr = 0 d.makefunccanrecover = false } // deferprocStack queues a new deferred function with a defer record on the stack. // The defer record, d, does not need to be initialized. // Other arguments are the same as in deferproc. //go:nosplit func deferprocStack(d *_defer, frame *bool, pfn uintptr, arg unsafe.Pointer) { gp := getg() if gp.m.curg != gp { // go code on the system stack can't defer throw("defer on system stack") } d.pfn = pfn d.retaddr = 0 d.makefunccanrecover = false d.heap = false // The lines below implement: // d.frame = frame // d.arg = arg // d._panic = nil // d.panicStack = gp._panic // d.link = gp._defer // But without write barriers. They are writes to the stack so they // don't need a write barrier, and furthermore are to uninitialized // memory, so they must not use a write barrier. *(*uintptr)(unsafe.Pointer(&d.frame)) = uintptr(unsafe.Pointer(frame)) *(*uintptr)(unsafe.Pointer(&d.arg)) = uintptr(unsafe.Pointer(arg)) *(*uintptr)(unsafe.Pointer(&d._panic)) = 0 *(*uintptr)(unsafe.Pointer(&d.panicStack)) = uintptr(unsafe.Pointer(gp._panic)) *(*uintptr)(unsafe.Pointer(&d.link)) = uintptr(unsafe.Pointer(gp._defer)) gp._defer = d } // Allocate a Defer, usually using per-P pool. // Each defer must be released with freedefer. func newdefer() *_defer { var d *_defer gp := getg() pp := gp.m.p.ptr() if len(pp.deferpool) == 0 && sched.deferpool != nil { systemstack(func() { lock(&sched.deferlock) for len(pp.deferpool) < cap(pp.deferpool)/2 && sched.deferpool != nil { d := sched.deferpool sched.deferpool = d.link d.link = nil pp.deferpool = append(pp.deferpool, d) } unlock(&sched.deferlock) }) } if n := len(pp.deferpool); n > 0 { d = pp.deferpool[n-1] pp.deferpool[n-1] = nil pp.deferpool = pp.deferpool[:n-1] } if d == nil { systemstack(func() { d = new(_defer) }) } d.heap = true return d } // Free the given defer. // The defer cannot be used after this call. // // This must not grow the stack because there may be a frame without a // stack map when this is called. // //go:nosplit func freedefer(d *_defer) { if d._panic != nil { freedeferpanic() } if d.pfn != 0 { freedeferfn() } if !d.heap { return } pp := getg().m.p.ptr() if len(pp.deferpool) == cap(pp.deferpool) { // Transfer half of local cache to the central cache. // // Take this slow path on the system stack so // we don't grow freedefer's stack. systemstack(func() { var first, last *_defer for len(pp.deferpool) > cap(pp.deferpool)/2 { n := len(pp.deferpool) d := pp.deferpool[n-1] pp.deferpool[n-1] = nil pp.deferpool = pp.deferpool[:n-1] if first == nil { first = d } else { last.link = d } last = d } lock(&sched.deferlock) last.link = sched.deferpool sched.deferpool = first unlock(&sched.deferlock) }) } // These lines used to be simply `*d = _defer{}` but that // started causing a nosplit stack overflow via typedmemmove. d.link = nil d.frame = nil d.panicStack = nil d.arg = nil d.retaddr = 0 d.makefunccanrecover = false // d._panic and d.pfn must be nil already. // If not, we would have called freedeferpanic or freedeferfn above, // both of which throw. pp.deferpool = append(pp.deferpool, d) } // Separate function so that it can split stack. // Windows otherwise runs out of stack space. func freedeferpanic() { // _panic must be cleared before d is unlinked from gp. throw("freedefer with d._panic != nil") } func freedeferfn() { // fn must be cleared before d is unlinked from gp. throw("freedefer with d.fn != nil") } // deferreturn is called to undefer the stack. // The compiler inserts a call to this function as a finally clause // wrapped around the body of any function that calls defer. // The frame argument points to the stack frame of the function. func deferreturn(frame *bool) { gp := getg() for gp._defer != nil && gp._defer.frame == frame { d := gp._defer pfn := d.pfn d.pfn = 0 if pfn != 0 { // This is rather awkward. // The gc compiler does this using assembler // code in jmpdefer. var fn func(unsafe.Pointer) *(*uintptr)(unsafe.Pointer(&fn)) = uintptr(noescape(unsafe.Pointer(&pfn))) gp.deferring = true fn(d.arg) gp.deferring = false } // If that was CgocallBackDone, it will have freed the // defer for us, since we are no longer running as Go code. if getg() == nil { *frame = true return } if gp.ranCgocallBackDone { gp.ranCgocallBackDone = false *frame = true return } gp._defer = d.link freedefer(d) // Since we are executing a defer function now, we // know that we are returning from the calling // function. If the calling function, or one of its // callees, panicked, then the defer functions would // be executed by panic. *frame = true } } // __builtin_extract_return_addr is a GCC intrinsic that converts an // address returned by __builtin_return_address(0) to a real address. // On most architectures this is a nop. //extern __builtin_extract_return_addr func __builtin_extract_return_addr(uintptr) uintptr // setdeferretaddr records the address to which the deferred function // returns. This is check by canrecover. The frontend relies on this // function returning false. func setdeferretaddr(retaddr uintptr) bool { gp := getg() if gp._defer != nil { gp._defer.retaddr = __builtin_extract_return_addr(retaddr) } return false } // checkdefer is called by exception handlers used when unwinding the // stack after a recovered panic. The exception handler is simply // checkdefer(frame) // return; // If we have not yet reached the frame we are looking for, we // continue unwinding. func checkdefer(frame *bool) { gp := getg() if gp == nil { // We should never wind up here. Even if some other // language throws an exception, the cgo code // should ensure that g is set. throw("no g in checkdefer") } else if gp.isforeign { // Some other language has thrown an exception. // We need to run the local defer handlers. // If they call recover, we stop unwinding here. var p _panic p.isforeign = true p.link = gp._panic gp._panic = (*_panic)(noescape(unsafe.Pointer(&p))) for { d := gp._defer if d == nil || d.frame != frame || d.pfn == 0 { break } pfn := d.pfn gp._defer = d.link var fn func(unsafe.Pointer) *(*uintptr)(unsafe.Pointer(&fn)) = uintptr(noescape(unsafe.Pointer(&pfn))) gp.deferring = true fn(d.arg) gp.deferring = false freedefer(d) if p.recovered { // The recover function caught the panic // thrown by some other language. break } } recovered := p.recovered gp._panic = p.link if recovered { // Just return and continue executing Go code. *frame = true return } // We are panicking through this function. *frame = false } else if gp._defer != nil && gp._defer.pfn == 0 && gp._defer.frame == frame { // This is the defer function that called recover. // Simply return to stop the stack unwind, and let the // Go code continue to execute. d := gp._defer gp._defer = d.link freedefer(d) // We are returning from this function. *frame = true return } // This is some other defer function. It was already run by // the call to panic, or just above. Rethrow the exception. rethrowException() throw("rethrowException returned") } // unwindStack starts unwinding the stack for a panic. We unwind // function calls until we reach the one which used a defer function // which called recover. Each function which uses a defer statement // will have an exception handler, as shown above for checkdefer. func unwindStack() { // Allocate the exception type used by the unwind ABI. // It would be nice to define it in runtime_sysinfo.go, // but current definitions don't work because the required // alignment is larger than can be represented in Go. // The type never contains any Go pointers. size := unwindExceptionSize() usize := uintptr(unsafe.Sizeof(uintptr(0))) c := (size + usize - 1) / usize s := make([]uintptr, c) getg().exception = unsafe.Pointer(&s[0]) throwException() } // Goexit terminates the goroutine that calls it. No other goroutine is affected. // Goexit runs all deferred calls before terminating the goroutine. Because Goexit // is not a panic, any recover calls in those deferred functions will return nil. // // Calling Goexit from the main goroutine terminates that goroutine // without func main returning. Since func main has not returned, // the program continues execution of other goroutines. // If all other goroutines exit, the program crashes. func Goexit() { // Run all deferred functions for the current goroutine. // This code is similar to gopanic, see that implementation // for detailed comments. gp := getg() gp.goexiting = true // Create a panic object for Goexit, so we can recognize when it might be // bypassed by a recover(). var p _panic p.goexit = true p.link = gp._panic gp._panic = (*_panic)(noescape(unsafe.Pointer(&p))) for { d := gp._defer if d == nil { break } pfn := d.pfn if pfn == 0 { if d._panic != nil { d._panic.aborted = true d._panic = nil } gp._defer = d.link freedefer(d) continue } d.pfn = 0 var fn func(unsafe.Pointer) *(*uintptr)(unsafe.Pointer(&fn)) = uintptr(noescape(unsafe.Pointer(&pfn))) gp.deferring = true fn(d.arg) gp.deferring = false if gp._defer != d { throw("bad defer entry in Goexit") } d._panic = nil gp._defer = d.link freedefer(d) // Note: we ignore recovers here because Goexit isn't a panic } gp.goexiting = false goexit1() } // Call all Error and String methods before freezing the world. // Used when crashing with panicking. func preprintpanics(p *_panic) { defer func() { if recover() != nil { throw("panic while printing panic value") } }() for p != nil { switch v := p.arg.(type) { case error: p.arg = v.Error() case stringer: p.arg = v.String() } p = p.link } } // Print all currently active panics. Used when crashing. // Should only be called after preprintpanics. func printpanics(p *_panic) { if p.link != nil { printpanics(p.link) if !p.link.goexit { print("\t") } } if p.goexit { return } print("panic: ") printany(p.arg) if p.recovered { print(" [recovered]") } print("\n") } // The implementation of the predeclared function panic. func gopanic(e interface{}) { gp := getg() if gp.m.curg != gp { print("panic: ") printany(e) print("\n") throw("panic on system stack") } if gp.m.mallocing != 0 { print("panic: ") printany(e) print("\n") throw("panic during malloc") } if gp.m.preemptoff != "" { print("panic: ") printany(e) print("\n") print("preempt off reason: ") print(gp.m.preemptoff) print("\n") throw("panic during preemptoff") } if gp.m.locks != 0 { print("panic: ") printany(e) print("\n") throw("panic holding locks") } // The gc compiler allocates this new _panic struct on the // stack. We can't do that, because when a deferred function // recovers the panic we unwind the stack. We unlink this // entry before unwinding the stack, but that doesn't help in // the case where we panic, a deferred function recovers and // then panics itself, that panic is in turn recovered, and // unwinds the stack past this stack frame. p := &_panic{ arg: e, link: gp._panic, } gp._panic = p atomic.Xadd(&runningPanicDefers, 1) for { d := gp._defer if d == nil { break } pfn := d.pfn // If defer was started by earlier panic or Goexit (and, since we're back here, that triggered a new panic), // take defer off list. The earlier panic or Goexit will not continue running. if pfn == 0 { if d._panic != nil { d._panic.aborted = true } d._panic = nil gp._defer = d.link freedefer(d) continue } d.pfn = 0 // Record the panic that is running the defer. // If there is a new panic during the deferred call, that panic // will find d in the list and will mark d._panic (this panic) aborted. d._panic = p var fn func(unsafe.Pointer) *(*uintptr)(unsafe.Pointer(&fn)) = uintptr(noescape(unsafe.Pointer(&pfn))) gp.deferring = true fn(d.arg) gp.deferring = false if gp._defer != d { throw("bad defer entry in panic") } d._panic = nil if p.recovered { gp._panic = p.link if gp._panic != nil && gp._panic.goexit && gp._panic.aborted { Goexit() throw("Goexit returned") } atomic.Xadd(&runningPanicDefers, -1) // Aborted panics are marked but remain on the g.panic list. // Remove them from the list. for gp._panic != nil && gp._panic.aborted { gp._panic = gp._panic.link } if gp._panic == nil { // must be done with signal gp.sig = 0 } if gp._panic != nil && gp._panic.goexit { Goexit() throw("Goexit returned") } // Unwind the stack by throwing an exception. // The compiler has arranged to create // exception handlers in each function // that uses a defer statement. These // exception handlers will check whether // the entry on the top of the defer stack // is from the current function. If it is, // we have unwound the stack far enough. unwindStack() throw("unwindStack returned") } // Because we executed that defer function by a panic, // and it did not call recover, we know that we are // not returning from the calling function--we are // panicking through it. *d.frame = false // Deferred function did not panic. Remove d. // In the p.recovered case, d will be removed by checkdefer. gp._defer = d.link freedefer(d) } // ran out of deferred calls - old-school panic now // Because it is unsafe to call arbitrary user code after freezing // the world, we call preprintpanics to invoke all necessary Error // and String methods to prepare the panic strings before startpanic. preprintpanics(gp._panic) fatalpanic(gp._panic) // should not return *(*int)(nil) = 0 // not reached } // currentDefer returns the top of the defer stack if it can be recovered. // Otherwise it returns nil. func currentDefer() *_defer { gp := getg() d := gp._defer if d == nil { return nil } // The panic that would be recovered is the one on the top of // the panic stack. We do not want to recover it if that panic // was on the top of the panic stack when this function was // deferred. if d.panicStack == gp._panic { return nil } // The deferred thunk will call setdeferretaddr. If this has // not happened, then we have not been called via defer, and // we can not recover. if d.retaddr == 0 { return nil } return d } // canrecover is called by a thunk to see if the real function would // be permitted to recover a panic value. Recovering a value is // permitted if the thunk was called directly by defer. retaddr is the // return address of the function that is calling canrecover--that is, // the thunk. func canrecover(retaddr uintptr) bool { d := currentDefer() if d == nil { return false } ret := __builtin_extract_return_addr(retaddr) dret := d.retaddr if ret <= dret && ret+16 >= dret { return true } // On some systems, in some cases, the return address does not // work reliably. See http://gcc.gnu.org/PR60406. If we are // permitted to call recover, the call stack will look like this: // runtime.gopanic, runtime.deferreturn, etc. // thunk to call deferred function (calls __go_set_defer_retaddr) // function that calls __go_can_recover (passing return address) // runtime.canrecover // Calling callers will skip the thunks. So if our caller's // caller starts with "runtime.", then we are permitted to // call recover. var locs [16]location if callers(1, locs[:2]) < 2 { return false } name := locs[1].function if hasPrefix(name, "runtime.") { return true } // If the function calling recover was created by reflect.MakeFunc, // then makefuncfficanrecover will have set makefunccanrecover. if !d.makefunccanrecover { return false } // We look up the stack, ignoring libffi functions and // functions in the reflect package, until we find // reflect.makeFuncStub or reflect.ffi_callback called by FFI // functions. Then we check the caller of that function. n := callers(2, locs[:]) foundFFICallback := false i := 0 for ; i < n; i++ { name = locs[i].function if name == "" { // No function name means this caller isn't Go code. // Assume that this is libffi. continue } // Ignore function in libffi. if hasPrefix(name, "ffi_") { continue } if foundFFICallback { break } if name == "reflect.ffi_callback" { foundFFICallback = true continue } // Ignore other functions in the reflect package. if hasPrefix(name, "reflect.") || hasPrefix(name, ".1reflect.") { continue } // We should now be looking at the real caller. break } if i < n { name = locs[i].function if hasPrefix(name, "runtime.") { return true } } return false } // This function is called when code is about to enter a function // created by the libffi version of reflect.MakeFunc. This function is // passed the names of the callers of the libffi code that called the // stub. It uses them to decide whether it is permitted to call // recover, and sets d.makefunccanrecover so that gorecover can make // the same decision. func makefuncfficanrecover(loc []location) { d := currentDefer() if d == nil { return } // If we are already in a call stack of MakeFunc functions, // there is nothing we can usefully check here. if d.makefunccanrecover { return } // loc starts with the caller of our caller. That will be a thunk. // If its caller was a function function, then it was called // directly by defer. if len(loc) < 2 { return } name := loc[1].function if hasPrefix(name, "runtime.") { d.makefunccanrecover = true } } // makefuncreturning is called when code is about to exit a function // created by reflect.MakeFunc. It is called by the function stub used // by reflect.MakeFunc. It clears the makefunccanrecover field. It's // OK to always clear this field, because canrecover will only be // called by a stub created for a function that calls recover. That // stub will not call a function created by reflect.MakeFunc, so by // the time we get here any caller higher up on the call stack no // longer needs the information. func makefuncreturning() { d := getg()._defer if d != nil { d.makefunccanrecover = false } } // The implementation of the predeclared function recover. func gorecover() interface{} { gp := getg() p := gp._panic if p != nil && !p.goexit && !p.recovered { p.recovered = true return p.arg } return nil } // deferredrecover is called when a call to recover is deferred. That // is, something like // defer recover() // // We need to handle this specially. In gc, the recover function // looks up the stack frame. In particular, that means that a deferred // recover will not recover a panic thrown in the same function that // defers the recover. It will only recover a panic thrown in a // function that defers the deferred call to recover. // // In other words: // // func f1() { // defer recover() // does not stop panic // panic(0) // } // // func f2() { // defer func() { // defer recover() // stops panic(0) // }() // panic(0) // } // // func f3() { // defer func() { // defer recover() // does not stop panic // panic(0) // }() // panic(1) // } // // func f4() { // defer func() { // defer func() { // defer recover() // stops panic(0) // }() // panic(0) // }() // panic(1) // } // // The interesting case here is f3. As can be seen from f2, the // deferred recover could pick up panic(1). However, this does not // happen because it is blocked by the panic(0). // // When a function calls recover, then when we invoke it we pass a // hidden parameter indicating whether it should recover something. // This parameter is set based on whether the function is being // invoked directly from defer. The parameter winds up determining // whether __go_recover or __go_deferred_recover is called at all. // // In the case of a deferred recover, the hidden parameter that // controls the call is actually the one set up for the function that // runs the defer recover() statement. That is the right thing in all // the cases above except for f3. In f3 the function is permitted to // call recover, but the deferred recover call is not. We address that // here by checking for that specific case before calling recover. If // this function was deferred when there is already a panic on the // panic stack, then we can only recover that panic, not any other. // Note that we can get away with using a special function here // because you are not permitted to take the address of a predeclared // function like recover. func deferredrecover() interface{} { gp := getg() if gp._defer == nil || gp._defer.panicStack != gp._panic { return nil } return gorecover() } //go:linkname sync_throw sync.throw func sync_throw(s string) { throw(s) } //go:nosplit func throw(s string) { // Everything throw does should be recursively nosplit so it // can be called even when it's unsafe to grow the stack. systemstack(func() { print("fatal error: ", s, "\n") }) gp := getg() if gp.m.throwing == 0 { gp.m.throwing = 1 } fatalthrow() *(*int)(nil) = 0 // not reached } // runningPanicDefers is non-zero while running deferred functions for panic. // runningPanicDefers is incremented and decremented atomically. // This is used to try hard to get a panic stack trace out when exiting. var runningPanicDefers uint32 // panicking is non-zero when crashing the program for an unrecovered panic. // panicking is incremented and decremented atomically. var panicking uint32 // paniclk is held while printing the panic information and stack trace, // so that two concurrent panics don't overlap their output. var paniclk mutex // fatalthrow implements an unrecoverable runtime throw. It freezes the // system, prints stack traces starting from its caller, and terminates the // process. // //go:nosplit func fatalthrow() { pc := getcallerpc() sp := getcallersp() gp := getg() startpanic_m() if dopanic_m(gp, pc, sp) { crash() } exit(2) *(*int)(nil) = 0 // not reached } // fatalpanic implements an unrecoverable panic. It is like fatalthrow, except // that if msgs != nil, fatalpanic also prints panic messages and decrements // runningPanicDefers once main is blocked from exiting. // //go:nosplit func fatalpanic(msgs *_panic) { pc := getcallerpc() sp := getcallersp() gp := getg() var docrash bool if startpanic_m() && msgs != nil { // There were panic messages and startpanic_m // says it's okay to try to print them. // startpanic_m set panicking, which will // block main from exiting, so now OK to // decrement runningPanicDefers. atomic.Xadd(&runningPanicDefers, -1) printpanics(msgs) } docrash = dopanic_m(gp, pc, sp) if docrash { // By crashing outside the above systemstack call, debuggers // will not be confused when generating a backtrace. // Function crash is marked nosplit to avoid stack growth. crash() } systemstack(func() { exit(2) }) *(*int)(nil) = 0 // not reached } // startpanic_m prepares for an unrecoverable panic. // // It returns true if panic messages should be printed, or false if // the runtime is in bad shape and should just print stacks. // // It must not have write barriers even though the write barrier // explicitly ignores writes once dying > 0. Write barriers still // assume that g.m.p != nil, and this function may not have P // in some contexts (e.g. a panic in a signal handler for a signal // sent to an M with no P). // //go:nowritebarrierrec func startpanic_m() bool { _g_ := getg() if mheap_.cachealloc.size == 0 { // very early print("runtime: panic before malloc heap initialized\n") } // Disallow malloc during an unrecoverable panic. A panic // could happen in a signal handler, or in a throw, or inside // malloc itself. We want to catch if an allocation ever does // happen (even if we're not in one of these situations). _g_.m.mallocing++ // If we're dying because of a bad lock count, set it to a // good lock count so we don't recursively panic below. if _g_.m.locks < 0 { _g_.m.locks = 1 } switch _g_.m.dying { case 0: // Setting dying >0 has the side-effect of disabling this G's writebuf. _g_.m.dying = 1 atomic.Xadd(&panicking, 1) lock(&paniclk) if debug.schedtrace > 0 || debug.scheddetail > 0 { schedtrace(true) } freezetheworld() return true case 1: // Something failed while panicking. // Just print a stack trace and exit. _g_.m.dying = 2 print("panic during panic\n") return false case 2: // This is a genuine bug in the runtime, we couldn't even // print the stack trace successfully. _g_.m.dying = 3 print("stack trace unavailable\n") exit(4) fallthrough default: // Can't even print! Just exit. exit(5) return false // Need to return something. } } var didothers bool var deadlock mutex func dopanic_m(gp *g, pc, sp uintptr) bool { if gp.sig != 0 { signame := signame(gp.sig) if signame != "" { print("[signal ", signame) } else { print("[signal ", hex(gp.sig)) } print(" code=", hex(gp.sigcode0), " addr=", hex(gp.sigcode1), " pc=", hex(gp.sigpc), "]\n") } level, all, docrash := gotraceback() _g_ := getg() if level > 0 { if gp != gp.m.curg { all = true } if gp != gp.m.g0 { print("\n") goroutineheader(gp) traceback(0) } else if level >= 2 || _g_.m.throwing > 0 { print("\nruntime stack:\n") traceback(0) } if !didothers && all { didothers = true tracebackothers(gp) } } unlock(&paniclk) if atomic.Xadd(&panicking, -1) != 0 { // Some other m is panicking too. // Let it print what it needs to print. // Wait forever without chewing up cpu. // It will exit when it's done. lock(&deadlock) lock(&deadlock) } printDebugLog() return docrash } // canpanic returns false if a signal should throw instead of // panicking. // //go:nosplit func canpanic(gp *g) bool { // Note that g is m->gsignal, different from gp. // Note also that g->m can change at preemption, so m can go stale // if this function ever makes a function call. _g_ := getg() _m_ := _g_.m // Is it okay for gp to panic instead of crashing the program? // Yes, as long as it is running Go code, not runtime code, // and not stuck in a system call. if gp == nil || gp != _m_.curg { return false } if _m_.locks != 0 || _m_.mallocing != 0 || _m_.throwing != 0 || _m_.preemptoff != "" || _m_.dying != 0 { return false } status := readgstatus(gp) if status&^_Gscan != _Grunning || gp.syscallsp != 0 { return false } return true } // isAbortPC reports whether pc is the program counter at which // runtime.abort raises a signal. // // It is nosplit because it's part of the isgoexception // implementation. // //go:nosplit func isAbortPC(pc uintptr) bool { return false }