Home | History | Annotate | Download | only in runtime
      1 // Copyright 2013 The Go Authors.  All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build darwin dragonfly freebsd linux nacl netbsd openbsd
      6 
      7 package runtime
      8 
      9 import "unsafe"
     10 
     11 func dumpregs(c *sigctxt) {
     12 	print("eax    ", hex(c.eax()), "\n")
     13 	print("ebx    ", hex(c.ebx()), "\n")
     14 	print("ecx    ", hex(c.ecx()), "\n")
     15 	print("edx    ", hex(c.edx()), "\n")
     16 	print("edi    ", hex(c.edi()), "\n")
     17 	print("esi    ", hex(c.esi()), "\n")
     18 	print("ebp    ", hex(c.ebp()), "\n")
     19 	print("esp    ", hex(c.esp()), "\n")
     20 	print("eip    ", hex(c.eip()), "\n")
     21 	print("eflags ", hex(c.eflags()), "\n")
     22 	print("cs     ", hex(c.cs()), "\n")
     23 	print("fs     ", hex(c.fs()), "\n")
     24 	print("gs     ", hex(c.gs()), "\n")
     25 }
     26 
     27 var crashing int32
     28 
     29 // May run during STW, so write barriers are not allowed.
     30 //go:nowritebarrier
     31 func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
     32 	_g_ := getg()
     33 	c := &sigctxt{info, ctxt}
     34 
     35 	if sig == _SIGPROF {
     36 		sigprof(uintptr(c.eip()), uintptr(c.esp()), 0, gp, _g_.m)
     37 		return
     38 	}
     39 
     40 	flags := int32(_SigThrow)
     41 	if sig < uint32(len(sigtable)) {
     42 		flags = sigtable[sig].flags
     43 	}
     44 	if c.sigcode() != _SI_USER && flags&_SigPanic != 0 {
     45 		// Make it look like a call to the signal func.
     46 		// Have to pass arguments out of band since
     47 		// augmenting the stack frame would break
     48 		// the unwinding code.
     49 		gp.sig = sig
     50 		gp.sigcode0 = uintptr(c.sigcode())
     51 		gp.sigcode1 = uintptr(c.sigaddr())
     52 		gp.sigpc = uintptr(c.eip())
     53 
     54 		if GOOS == "darwin" {
     55 			// Work around Leopard bug that doesn't set FPE_INTDIV.
     56 			// Look at instruction to see if it is a divide.
     57 			// Not necessary in Snow Leopard (si_code will be != 0).
     58 			if sig == _SIGFPE && gp.sigcode0 == 0 {
     59 				pc := (*[4]byte)(unsafe.Pointer(gp.sigpc))
     60 				i := 0
     61 				if pc[i] == 0x66 { // 16-bit instruction prefix
     62 					i++
     63 				}
     64 				if pc[i] == 0xF6 || pc[i] == 0xF7 {
     65 					gp.sigcode0 = _FPE_INTDIV
     66 				}
     67 			}
     68 		}
     69 
     70 		pc := uintptr(c.eip())
     71 		sp := uintptr(c.esp())
     72 
     73 		// If we don't recognize the PC as code
     74 		// but we do recognize the top pointer on the stack as code,
     75 		// then assume this was a call to non-code and treat like
     76 		// pc == 0, to make unwinding show the context.
     77 		if pc != 0 && findfunc(pc) == nil && findfunc(*(*uintptr)(unsafe.Pointer(sp))) != nil {
     78 			pc = 0
     79 		}
     80 
     81 		// Only push runtime.sigpanic if pc != 0.
     82 		// If pc == 0, probably panicked because of a
     83 		// call to a nil func.  Not pushing that onto sp will
     84 		// make the trace look like a call to runtime.sigpanic instead.
     85 		// (Otherwise the trace will end at runtime.sigpanic and we
     86 		// won't get to see who faulted.)
     87 		if pc != 0 {
     88 			if regSize > ptrSize {
     89 				sp -= ptrSize
     90 				*(*uintptr)(unsafe.Pointer(sp)) = 0
     91 			}
     92 			sp -= ptrSize
     93 			*(*uintptr)(unsafe.Pointer(sp)) = pc
     94 			c.set_esp(uint32(sp))
     95 		}
     96 		c.set_eip(uint32(funcPC(sigpanic)))
     97 		return
     98 	}
     99 
    100 	if c.sigcode() == _SI_USER || flags&_SigNotify != 0 {
    101 		if sigsend(sig) {
    102 			return
    103 		}
    104 	}
    105 
    106 	if flags&_SigKill != 0 {
    107 		exit(2)
    108 	}
    109 
    110 	if flags&_SigThrow == 0 {
    111 		return
    112 	}
    113 
    114 	_g_.m.throwing = 1
    115 	_g_.m.caughtsig.set(gp)
    116 
    117 	if crashing == 0 {
    118 		startpanic()
    119 	}
    120 
    121 	if sig < uint32(len(sigtable)) {
    122 		print(sigtable[sig].name, "\n")
    123 	} else {
    124 		print("Signal ", sig, "\n")
    125 	}
    126 
    127 	print("PC=", hex(c.eip()), " m=", _g_.m.id, "\n")
    128 	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
    129 		print("signal arrived during cgo execution\n")
    130 		gp = _g_.m.lockedg
    131 	}
    132 	print("\n")
    133 
    134 	var docrash bool
    135 	if gotraceback(&docrash) > 0 {
    136 		goroutineheader(gp)
    137 
    138 		// On Linux/386, all system calls go through the vdso kernel_vsyscall routine.
    139 		// Normally we don't see those PCs, but during signals we can.
    140 		// If we see a PC in the vsyscall area (it moves around, but near the top of memory),
    141 		// assume we're blocked in the vsyscall routine, which has saved
    142 		// three words on the stack after the initial call saved the caller PC.
    143 		// Pop all four words off SP and use the saved PC.
    144 		// The check of the stack bounds here should suffice to avoid a fault
    145 		// during the actual PC pop.
    146 		// If we do load a bogus PC, not much harm done: we weren't going
    147 		// to get a decent traceback anyway.
    148 		// TODO(rsc): Make this more precise: we should do more checks on the PC,
    149 		// and we should find out whether different versions of the vdso page
    150 		// use different prologues that store different amounts on the stack.
    151 		pc := uintptr(c.eip())
    152 		sp := uintptr(c.esp())
    153 		if GOOS == "linux" && pc >= 0xf4000000 && gp.stack.lo <= sp && sp+16 <= gp.stack.hi {
    154 			// Assume in vsyscall page.
    155 			sp += 16
    156 			pc = *(*uintptr)(unsafe.Pointer(sp - 4))
    157 			print("runtime: unwind vdso kernel_vsyscall: pc=", hex(pc), " sp=", hex(sp), "\n")
    158 		}
    159 
    160 		tracebacktrap(pc, sp, 0, gp)
    161 		if crashing > 0 && gp != _g_.m.curg && _g_.m.curg != nil && readgstatus(_g_.m.curg)&^_Gscan == _Grunning {
    162 			// tracebackothers on original m skipped this one; trace it now.
    163 			goroutineheader(_g_.m.curg)
    164 			traceback(^uintptr(0), ^uintptr(0), 0, gp)
    165 		} else if crashing == 0 {
    166 			tracebackothers(gp)
    167 			print("\n")
    168 		}
    169 		dumpregs(c)
    170 	}
    171 
    172 	if docrash {
    173 		crashing++
    174 		if crashing < sched.mcount {
    175 			// There are other m's that need to dump their stacks.
    176 			// Relay SIGQUIT to the next m by sending it to the current process.
    177 			// All m's that have already received SIGQUIT have signal masks blocking
    178 			// receipt of any signals, so the SIGQUIT will go to an m that hasn't seen it yet.
    179 			// When the last m receives the SIGQUIT, it will fall through to the call to
    180 			// crash below. Just in case the relaying gets botched, each m involved in
    181 			// the relay sleeps for 5 seconds and then does the crash/exit itself.
    182 			// In expected operation, the last m has received the SIGQUIT and run
    183 			// crash/exit and the process is gone, all long before any of the
    184 			// 5-second sleeps have finished.
    185 			print("\n-----\n\n")
    186 			raiseproc(_SIGQUIT)
    187 			usleep(5 * 1000 * 1000)
    188 		}
    189 		crash()
    190 	}
    191 
    192 	exit(2)
    193 }
    194