1// Copyright 2011 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
5package runtime
6
7import (
8	"unsafe"
9)
10
11func disableWER() {
12	// do not display Windows Error Reporting dialogue
13	const (
14		SEM_FAILCRITICALERRORS     = 0x0001
15		SEM_NOGPFAULTERRORBOX      = 0x0002
16		SEM_NOALIGNMENTFAULTEXCEPT = 0x0004
17		SEM_NOOPENFILEERRORBOX     = 0x8000
18	)
19	errormode := uint32(stdcall1(_SetErrorMode, SEM_NOGPFAULTERRORBOX))
20	stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX)
21}
22
23// in sys_windows_386.s and sys_windows_amd64.s
24func exceptiontramp()
25func firstcontinuetramp()
26func lastcontinuetramp()
27
28func initExceptionHandler() {
29	stdcall2(_AddVectoredExceptionHandler, 1, funcPC(exceptiontramp))
30	if _AddVectoredContinueHandler == nil || GOARCH == "386" {
31		// use SetUnhandledExceptionFilter for windows-386 or
32		// if VectoredContinueHandler is unavailable.
33		// note: SetUnhandledExceptionFilter handler won't be called, if debugging.
34		stdcall1(_SetUnhandledExceptionFilter, funcPC(lastcontinuetramp))
35	} else {
36		stdcall2(_AddVectoredContinueHandler, 1, funcPC(firstcontinuetramp))
37		stdcall2(_AddVectoredContinueHandler, 0, funcPC(lastcontinuetramp))
38	}
39}
40
41// isAbort returns true, if context r describes exception raised
42// by calling runtime.abort function.
43//
44//go:nosplit
45func isAbort(r *context) bool {
46	switch GOARCH {
47	case "386", "amd64":
48		// In the case of an abort, the exception IP is one byte after
49		// the INT3 (this differs from UNIX OSes).
50		return isAbortPC(r.ip() - 1)
51	case "arm":
52		return isAbortPC(r.ip())
53	default:
54		return false
55	}
56}
57
58// isgoexception reports whether this exception should be translated
59// into a Go panic.
60//
61// It is nosplit to avoid growing the stack in case we're aborting
62// because of a stack overflow.
63//
64//go:nosplit
65func isgoexception(info *exceptionrecord, r *context) bool {
66	// Only handle exception if executing instructions in Go binary
67	// (not Windows library code).
68	// TODO(mwhudson): needs to loop to support shared libs
69	if r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip() {
70		return false
71	}
72
73	if isAbort(r) {
74		// Never turn abort into a panic.
75		return false
76	}
77
78	// Go will only handle some exceptions.
79	switch info.exceptioncode {
80	default:
81		return false
82	case _EXCEPTION_ACCESS_VIOLATION:
83	case _EXCEPTION_INT_DIVIDE_BY_ZERO:
84	case _EXCEPTION_INT_OVERFLOW:
85	case _EXCEPTION_FLT_DENORMAL_OPERAND:
86	case _EXCEPTION_FLT_DIVIDE_BY_ZERO:
87	case _EXCEPTION_FLT_INEXACT_RESULT:
88	case _EXCEPTION_FLT_OVERFLOW:
89	case _EXCEPTION_FLT_UNDERFLOW:
90	case _EXCEPTION_BREAKPOINT:
91	}
92	return true
93}
94
95// Called by sigtramp from Windows VEH handler.
96// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
97// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
98//
99// This is the first entry into Go code for exception handling. This
100// is nosplit to avoid growing the stack until we've checked for
101// _EXCEPTION_BREAKPOINT, which is raised if we overflow the g0 stack,
102//
103//go:nosplit
104func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
105	if !isgoexception(info, r) {
106		return _EXCEPTION_CONTINUE_SEARCH
107	}
108
109	// After this point, it is safe to grow the stack.
110
111	if gp.throwsplit {
112		// We can't safely sigpanic because it may grow the
113		// stack. Let it fall through.
114		return _EXCEPTION_CONTINUE_SEARCH
115	}
116
117	// Make it look like a call to the signal func.
118	// Have to pass arguments out of band since
119	// augmenting the stack frame would break
120	// the unwinding code.
121	gp.sig = info.exceptioncode
122	gp.sigcode0 = uintptr(info.exceptioninformation[0])
123	gp.sigcode1 = uintptr(info.exceptioninformation[1])
124	gp.sigpc = r.ip()
125
126	// Only push runtime·sigpanic if r.ip() != 0.
127	// If r.ip() == 0, probably panicked because of a
128	// call to a nil func. Not pushing that onto sp will
129	// make the trace look like a call to runtime·sigpanic instead.
130	// (Otherwise the trace will end at runtime·sigpanic and we
131	// won't get to see who faulted.)
132	// Also don't push a sigpanic frame if the faulting PC
133	// is the entry of asyncPreempt. In this case, we suspended
134	// the thread right between the fault and the exception handler
135	// starting to run, and we have pushed an asyncPreempt call.
136	// The exception is not from asyncPreempt, so not to push a
137	// sigpanic call to make it look like that. Instead, just
138	// overwrite the PC. (See issue #35773)
139	if r.ip() != 0 && r.ip() != funcPC(asyncPreempt) {
140		sp := unsafe.Pointer(r.sp())
141		sp = add(sp, ^(unsafe.Sizeof(uintptr(0)) - 1)) // sp--
142		r.set_sp(uintptr(sp))
143		switch GOARCH {
144		default:
145			panic("unsupported architecture")
146		case "386", "amd64":
147			*((*uintptr)(sp)) = r.ip()
148		case "arm":
149			*((*uintptr)(sp)) = r.lr()
150			r.set_lr(r.ip())
151		}
152	}
153	r.set_ip(funcPC(sigpanic))
154	return _EXCEPTION_CONTINUE_EXECUTION
155}
156
157// It seems Windows searches ContinueHandler's list even
158// if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION.
159// firstcontinuehandler will stop that search,
160// if exceptionhandler did the same earlier.
161//
162// It is nosplit for the same reason as exceptionhandler.
163//
164//go:nosplit
165func firstcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
166	if !isgoexception(info, r) {
167		return _EXCEPTION_CONTINUE_SEARCH
168	}
169	return _EXCEPTION_CONTINUE_EXECUTION
170}
171
172var testingWER bool
173
174// lastcontinuehandler is reached, because runtime cannot handle
175// current exception. lastcontinuehandler will print crash info and exit.
176//
177// It is nosplit for the same reason as exceptionhandler.
178//
179//go:nosplit
180func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
181	if islibrary || isarchive {
182		// Go DLL/archive has been loaded in a non-go program.
183		// If the exception does not originate from go, the go runtime
184		// should not take responsibility of crashing the process.
185		return _EXCEPTION_CONTINUE_SEARCH
186	}
187	if testingWER {
188		return _EXCEPTION_CONTINUE_SEARCH
189	}
190
191	_g_ := getg()
192
193	if panicking != 0 { // traceback already printed
194		exit(2)
195	}
196	panicking = 1
197
198	// In case we're handling a g0 stack overflow, blow away the
199	// g0 stack bounds so we have room to print the traceback. If
200	// this somehow overflows the stack, the OS will trap it.
201	_g_.stack.lo = 0
202	_g_.stackguard0 = _g_.stack.lo + _StackGuard
203	_g_.stackguard1 = _g_.stackguard0
204
205	print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n")
206
207	print("PC=", hex(r.ip()), "\n")
208	if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
209		if iscgo {
210			print("signal arrived during external code execution\n")
211		}
212		gp = _g_.m.lockedg.ptr()
213	}
214	print("\n")
215
216	// TODO(jordanrh1): This may be needed for 386/AMD64 as well.
217	if GOARCH == "arm" {
218		_g_.m.throwing = 1
219		_g_.m.caughtsig.set(gp)
220	}
221
222	level, _, docrash := gotraceback()
223	if level > 0 {
224		tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
225		tracebackothers(gp)
226		dumpregs(r)
227	}
228
229	if docrash {
230		crash()
231	}
232
233	exit(2)
234	return 0 // not reached
235}
236
237func sigpanic() {
238	g := getg()
239	if !canpanic(g) {
240		throw("unexpected signal during runtime execution")
241	}
242
243	switch g.sig {
244	case _EXCEPTION_ACCESS_VIOLATION:
245		if g.sigcode1 < 0x1000 || g.paniconfault {
246			panicmem()
247		}
248		print("unexpected fault address ", hex(g.sigcode1), "\n")
249		throw("fault")
250	case _EXCEPTION_INT_DIVIDE_BY_ZERO:
251		panicdivide()
252	case _EXCEPTION_INT_OVERFLOW:
253		panicoverflow()
254	case _EXCEPTION_FLT_DENORMAL_OPERAND,
255		_EXCEPTION_FLT_DIVIDE_BY_ZERO,
256		_EXCEPTION_FLT_INEXACT_RESULT,
257		_EXCEPTION_FLT_OVERFLOW,
258		_EXCEPTION_FLT_UNDERFLOW:
259		panicfloat()
260	}
261	throw("fault")
262}
263
264var (
265	badsignalmsg [100]byte
266	badsignallen int32
267)
268
269func setBadSignalMsg() {
270	const msg = "runtime: signal received on thread not created by Go.\n"
271	for i, c := range msg {
272		badsignalmsg[i] = byte(c)
273		badsignallen++
274	}
275}
276
277// Following are not implemented.
278
279func initsig(preinit bool) {
280}
281
282func sigenable(sig uint32) {
283}
284
285func sigdisable(sig uint32) {
286}
287
288func sigignore(sig uint32) {
289}
290
291func badsignal2()
292
293func raisebadsignal(sig uint32) {
294	badsignal2()
295}
296
297func signame(sig uint32) string {
298	return ""
299}
300
301//go:nosplit
302func crash() {
303	// TODO: This routine should do whatever is needed
304	// to make the Windows program abort/crash as it
305	// would if Go was not intercepting signals.
306	// On Unix the routine would remove the custom signal
307	// handler and then raise a signal (like SIGABRT).
308	// Something like that should happen here.
309	// It's okay to leave this empty for now: if crash returns
310	// the ordinary exit-after-panic happens.
311}
312
313// gsignalStack is unused on Windows.
314type gsignalStack struct{}
315