github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/runtime/signal_386.go (about)

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