blob: 7f860a37e1ab476afe5b0aa335f053444498d4af [file] [log] [blame]
Alex Brainmanab4578a2014-11-20 12:24:03 +11001// Copyright 2009 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
11//go:cgo_import_dynamic runtime._AddVectoredExceptionHandler AddVectoredExceptionHandler "kernel32.dll"
12//go:cgo_import_dynamic runtime._CloseHandle CloseHandle "kernel32.dll"
13//go:cgo_import_dynamic runtime._CreateEventA CreateEventA "kernel32.dll"
14//go:cgo_import_dynamic runtime._CreateThread CreateThread "kernel32.dll"
15//go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA "kernel32.dll"
16//go:cgo_import_dynamic runtime._CryptAcquireContextW CryptAcquireContextW "advapi32.dll"
17//go:cgo_import_dynamic runtime._CryptGenRandom CryptGenRandom "advapi32.dll"
18//go:cgo_import_dynamic runtime._CryptReleaseContext CryptReleaseContext "advapi32.dll"
19//go:cgo_import_dynamic runtime._DuplicateHandle DuplicateHandle "kernel32.dll"
20//go:cgo_import_dynamic runtime._ExitProcess ExitProcess "kernel32.dll"
21//go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW "kernel32.dll"
22//go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW "kernel32.dll"
23//go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress "kernel32.dll"
24//go:cgo_import_dynamic runtime._GetStdHandle GetStdHandle "kernel32.dll"
25//go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo "kernel32.dll"
26//go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext "kernel32.dll"
27//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW "kernel32.dll"
28//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA "kernel32.dll"
29//go:cgo_import_dynamic runtime._NtWaitForSingleObject NtWaitForSingleObject "ntdll.dll"
30//go:cgo_import_dynamic runtime._ResumeThread ResumeThread "kernel32.dll"
31//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll"
Alex Brainman03d66372014-12-31 13:46:58 +110032//go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode "kernel32.dll"
Alex Brainmanab4578a2014-11-20 12:24:03 +110033//go:cgo_import_dynamic runtime._SetEvent SetEvent "kernel32.dll"
34//go:cgo_import_dynamic runtime._SetProcessPriorityBoost SetProcessPriorityBoost "kernel32.dll"
35//go:cgo_import_dynamic runtime._SetThreadPriority SetThreadPriority "kernel32.dll"
36//go:cgo_import_dynamic runtime._SetUnhandledExceptionFilter SetUnhandledExceptionFilter "kernel32.dll"
37//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer "kernel32.dll"
38//go:cgo_import_dynamic runtime._Sleep Sleep "kernel32.dll"
39//go:cgo_import_dynamic runtime._SuspendThread SuspendThread "kernel32.dll"
40//go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject "kernel32.dll"
41//go:cgo_import_dynamic runtime._WriteFile WriteFile "kernel32.dll"
42//go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod "winmm.dll"
43
44var (
45 _AddVectoredExceptionHandler,
46 _CloseHandle,
47 _CreateEventA,
48 _CreateThread,
49 _CreateWaitableTimerA,
50 _CryptAcquireContextW,
51 _CryptGenRandom,
52 _CryptReleaseContext,
53 _DuplicateHandle,
54 _ExitProcess,
55 _FreeEnvironmentStringsW,
56 _GetEnvironmentStringsW,
57 _GetProcAddress,
58 _GetStdHandle,
59 _GetSystemInfo,
60 _GetThreadContext,
61 _LoadLibraryW,
62 _LoadLibraryA,
63 _NtWaitForSingleObject,
64 _ResumeThread,
65 _SetConsoleCtrlHandler,
Alex Brainman03d66372014-12-31 13:46:58 +110066 _SetErrorMode,
Alex Brainmanab4578a2014-11-20 12:24:03 +110067 _SetEvent,
68 _SetProcessPriorityBoost,
69 _SetThreadPriority,
70 _SetUnhandledExceptionFilter,
71 _SetWaitableTimer,
72 _Sleep,
73 _SuspendThread,
74 _WaitForSingleObject,
75 _WriteFile,
76 _timeBeginPeriod stdFunction
77)
78
79var _GetQueuedCompletionStatusEx stdFunction
80
81// in sys_windows_386.s and sys_windows_amd64.s
82func externalthreadhandler()
83func exceptiontramp()
84func firstcontinuetramp()
85func lastcontinuetramp()
86
87//go:nosplit
88func getLoadLibrary() uintptr {
89 return uintptr(unsafe.Pointer(_LoadLibraryW))
90}
91
92//go:nosplit
93func getGetProcAddress() uintptr {
94 return uintptr(unsafe.Pointer(_GetProcAddress))
95}
96
97func getproccount() int32 {
98 var info systeminfo
99 stdcall1(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
100 return int32(info.dwnumberofprocessors)
101}
102
103const (
104 currentProcess = ^uintptr(0) // -1 = current process
105 currentThread = ^uintptr(1) // -2 = current thread
106)
107
Alex Brainman03d66372014-12-31 13:46:58 +1100108const (
109 SEM_FAILCRITICALERRORS = 0x0001
110 SEM_NOGPFAULTERRORBOX = 0x0002
111 SEM_NOALIGNMENTFAULTEXCEPT = 0x0004
112 SEM_NOOPENFILEERRORBOX = 0x8000
113)
114
Alex Brainmanab4578a2014-11-20 12:24:03 +1100115var (
116 kernel32Name = []byte("kernel32.dll\x00")
117 addVectoredContinueHandlerName = []byte("AddVectoredContinueHandler\x00")
118 getQueuedCompletionStatusExName = []byte("GetQueuedCompletionStatusEx\x00")
119)
120
121func osinit() {
122 setBadSignalMsg()
123
124 kernel32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32Name[0])))
125
Alex Brainman03d66372014-12-31 13:46:58 +1100126 // don't display the crash dialog
127 errormode := uint32(stdcall1(_SetErrorMode, SEM_NOGPFAULTERRORBOX))
128 stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX)
129
Alex Brainmanab4578a2014-11-20 12:24:03 +1100130 externalthreadhandlerp = funcPC(externalthreadhandler)
131
132 stdcall2(_AddVectoredExceptionHandler, 1, funcPC(exceptiontramp))
133 addVectoredContinueHandler := uintptr(0)
134 if kernel32 != 0 {
135 addVectoredContinueHandler = stdcall2(_GetProcAddress, kernel32, uintptr(unsafe.Pointer(&addVectoredContinueHandlerName[0])))
136 }
137 if addVectoredContinueHandler == 0 || unsafe.Sizeof(&kernel32) == 4 {
138 // use SetUnhandledExceptionFilter for windows-386 or
139 // if VectoredContinueHandler is unavailable.
140 // note: SetUnhandledExceptionFilter handler won't be called, if debugging.
141 stdcall1(_SetUnhandledExceptionFilter, funcPC(lastcontinuetramp))
142 } else {
143 stdcall2(stdFunction(unsafe.Pointer(addVectoredContinueHandler)), 1, funcPC(firstcontinuetramp))
144 stdcall2(stdFunction(unsafe.Pointer(addVectoredContinueHandler)), 0, funcPC(lastcontinuetramp))
145 }
146
147 stdcall2(_SetConsoleCtrlHandler, funcPC(ctrlhandler), 1)
148
149 stdcall1(_timeBeginPeriod, 1)
150
151 ncpu = getproccount()
152
153 // Windows dynamic priority boosting assumes that a process has different types
154 // of dedicated threads -- GUI, IO, computational, etc. Go processes use
155 // equivalent threads that all do a mix of GUI, IO, computations, etc.
156 // In such context dynamic priority boosting does nothing but harm, so we turn it off.
157 stdcall2(_SetProcessPriorityBoost, currentProcess, 1)
158
159 if kernel32 != 0 {
160 _GetQueuedCompletionStatusEx = stdFunction(unsafe.Pointer(stdcall2(_GetProcAddress, kernel32, uintptr(unsafe.Pointer(&getQueuedCompletionStatusExName[0])))))
161 }
162}
163
Alex Brainmanab4578a2014-11-20 12:24:03 +1100164//go:nosplit
Keith Randall6820be22014-12-09 14:40:40 -0800165func getRandomData(r []byte) {
Alex Brainmanab4578a2014-11-20 12:24:03 +1100166 const (
167 prov_rsa_full = 1
168 crypt_verifycontext = 0xF0000000
169 )
170 var handle uintptr
Keith Randall6820be22014-12-09 14:40:40 -0800171 n := 0
Alex Brainmanab4578a2014-11-20 12:24:03 +1100172 if stdcall5(_CryptAcquireContextW, uintptr(unsafe.Pointer(&handle)), 0, 0, prov_rsa_full, crypt_verifycontext) != 0 {
Keith Randall6820be22014-12-09 14:40:40 -0800173 if stdcall3(_CryptGenRandom, handle, uintptr(len(r)), uintptr(unsafe.Pointer(&r[0]))) != 0 {
174 n = len(r)
Alex Brainmanab4578a2014-11-20 12:24:03 +1100175 }
176 stdcall2(_CryptReleaseContext, handle, 0)
177 }
Keith Randall6820be22014-12-09 14:40:40 -0800178 extendRandom(r, n)
Alex Brainmanab4578a2014-11-20 12:24:03 +1100179}
180
181func goenvs() {
182 var p *uint16
183
184 env := (*uint16)(unsafe.Pointer(stdcall0(_GetEnvironmentStringsW)))
185
186 n := 0
187 for p = env; *p != 0; n++ {
Alex Brainman0a38b2c2014-11-21 12:15:18 +1100188 p = (*uint16)(add(unsafe.Pointer(p), uintptr(findnullw(p)+1)*unsafe.Sizeof(*p)))
Alex Brainmanab4578a2014-11-20 12:24:03 +1100189 }
190
191 envs = makeStringSlice(int(n))
192
193 p = env
194 for i := 0; i < n; i++ {
195 envs[i] = gostringw(p)
Alex Brainman0a38b2c2014-11-21 12:15:18 +1100196 p = (*uint16)(add(unsafe.Pointer(p), uintptr(findnullw(p)+1)*unsafe.Sizeof(*p)))
Alex Brainmanab4578a2014-11-20 12:24:03 +1100197 }
198
199 stdcall1(_FreeEnvironmentStringsW, uintptr(unsafe.Pointer(env)))
200}
201
202//go:nosplit
203func exit(code int32) {
204 stdcall1(_ExitProcess, uintptr(code))
205}
206
207//go:nosplit
208func write(fd uintptr, buf unsafe.Pointer, n int32) int32 {
209 const (
210 _STD_OUTPUT_HANDLE = ^uintptr(10) // -11
211 _STD_ERROR_HANDLE = ^uintptr(11) // -12
212 )
213 var handle uintptr
214 switch fd {
215 case 1:
216 handle = stdcall1(_GetStdHandle, _STD_OUTPUT_HANDLE)
217 case 2:
218 handle = stdcall1(_GetStdHandle, _STD_ERROR_HANDLE)
219 default:
220 // assume fd is real windows handle.
221 handle = fd
222 }
223 var written uint32
224 stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0)
225 return int32(written)
226}
227
228//go:nosplit
229func semasleep(ns int64) int32 {
230 // store ms in ns to save stack space
231 if ns < 0 {
232 ns = _INFINITE
233 } else {
234 ns = int64(timediv(ns, 1000000, nil))
235 if ns == 0 {
236 ns = 1
237 }
238 }
239 if stdcall2(_WaitForSingleObject, getg().m.waitsema, uintptr(ns)) != 0 {
240 return -1 // timeout
241 }
242 return 0
243}
244
245//go:nosplit
246func semawakeup(mp *m) {
247 stdcall1(_SetEvent, mp.waitsema)
248}
249
250//go:nosplit
251func semacreate() uintptr {
252 return stdcall4(_CreateEventA, 0, 0, 0, 0)
253}
254
255func newosproc(mp *m, stk unsafe.Pointer) {
256 const _STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000
257 thandle := stdcall6(_CreateThread, 0, 0x20000,
258 funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)),
259 _STACK_SIZE_PARAM_IS_A_RESERVATION, 0)
260 if thandle == 0 {
261 println("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", getlasterror(), ")")
Keith Randallb2a950b2014-12-27 20:58:00 -0800262 throw("runtime.newosproc")
Alex Brainmanab4578a2014-11-20 12:24:03 +1100263 }
264}
265
266// Called to initialize a new m (including the bootstrap m).
267// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
268func mpreinit(mp *m) {
269}
270
271// Called to initialize a new m (including the bootstrap m).
272// Called on the new thread, can not allocate memory.
273func minit() {
274 var thandle uintptr
275 stdcall7(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS)
276 atomicstoreuintptr(&getg().m.thread, thandle)
277}
278
279// Called from dropm to undo the effect of an minit.
280func unminit() {
281 tp := &getg().m.thread
282 stdcall1(_CloseHandle, *tp)
283 *tp = 0
284}
285
286// Described in http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/
287type _KSYSTEM_TIME struct {
288 LowPart uint32
289 High1Time int32
290 High2Time int32
291}
292
293const (
294 _INTERRUPT_TIME = 0x7ffe0008
295 _SYSTEM_TIME = 0x7ffe0014
296)
297
298//go:nosplit
299func systime(addr uintptr) int64 {
300 timeaddr := (*_KSYSTEM_TIME)(unsafe.Pointer(addr))
301
302 var t _KSYSTEM_TIME
303 for i := 1; i < 10000; i++ {
304 // these fields must be read in that order (see URL above)
305 t.High1Time = timeaddr.High1Time
306 t.LowPart = timeaddr.LowPart
307 t.High2Time = timeaddr.High2Time
308 if t.High1Time == t.High2Time {
309 return int64(t.High1Time)<<32 | int64(t.LowPart)
310 }
311 if (i % 100) == 0 {
312 osyield()
313 }
314 }
315 systemstack(func() {
Keith Randallb2a950b2014-12-27 20:58:00 -0800316 throw("interrupt/system time is changing too fast")
Alex Brainmanab4578a2014-11-20 12:24:03 +1100317 })
318 return 0
319}
320
321//go:nosplit
322func unixnano() int64 {
323 return (systime(_SYSTEM_TIME) - 116444736000000000) * 100
324}
325
326//go:nosplit
327func nanotime() int64 {
328 return systime(_INTERRUPT_TIME) * 100
329}
330
331// Calling stdcall on os stack.
332//go:nosplit
333func stdcall(fn stdFunction) uintptr {
334 gp := getg()
335 mp := gp.m
336 mp.libcall.fn = uintptr(unsafe.Pointer(fn))
337
338 if mp.profilehz != 0 {
339 // leave pc/sp for cpu profiler
340 mp.libcallg = gp
341 mp.libcallpc = getcallerpc(unsafe.Pointer(&fn))
342 // sp must be the last, because once async cpu profiler finds
343 // all three values to be non-zero, it will use them
344 mp.libcallsp = getcallersp(unsafe.Pointer(&fn))
345 }
346 asmcgocall(unsafe.Pointer(funcPC(asmstdcall)), unsafe.Pointer(&mp.libcall))
347 mp.libcallsp = 0
348 return mp.libcall.r1
349}
350
351//go:nosplit
352func stdcall0(fn stdFunction) uintptr {
353 mp := getg().m
354 mp.libcall.n = 0
355 mp.libcall.args = uintptr(noescape(unsafe.Pointer(&fn))) // it's unused but must be non-nil, otherwise crashes
356 return stdcall(fn)
357}
358
359//go:nosplit
360func stdcall1(fn stdFunction, a0 uintptr) uintptr {
361 mp := getg().m
362 mp.libcall.n = 1
363 mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
364 return stdcall(fn)
365}
366
367//go:nosplit
368func stdcall2(fn stdFunction, a0, a1 uintptr) uintptr {
369 mp := getg().m
370 mp.libcall.n = 2
371 mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
372 return stdcall(fn)
373}
374
375//go:nosplit
376func stdcall3(fn stdFunction, a0, a1, a2 uintptr) uintptr {
377 mp := getg().m
378 mp.libcall.n = 3
379 mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
380 return stdcall(fn)
381}
382
383//go:nosplit
384func stdcall4(fn stdFunction, a0, a1, a2, a3 uintptr) uintptr {
385 mp := getg().m
386 mp.libcall.n = 4
387 mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
388 return stdcall(fn)
389}
390
391//go:nosplit
392func stdcall5(fn stdFunction, a0, a1, a2, a3, a4 uintptr) uintptr {
393 mp := getg().m
394 mp.libcall.n = 5
395 mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
396 return stdcall(fn)
397}
398
399//go:nosplit
400func stdcall6(fn stdFunction, a0, a1, a2, a3, a4, a5 uintptr) uintptr {
401 mp := getg().m
402 mp.libcall.n = 6
403 mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
404 return stdcall(fn)
405}
406
407//go:nosplit
408func stdcall7(fn stdFunction, a0, a1, a2, a3, a4, a5, a6 uintptr) uintptr {
409 mp := getg().m
410 mp.libcall.n = 7
411 mp.libcall.args = uintptr(noescape(unsafe.Pointer(&a0)))
412 return stdcall(fn)
413}
414
415// in sys_windows_386.s and sys_windows_amd64.s
416func usleep1(usec uint32)
417
418//go:nosplit
419func osyield() {
420 usleep1(1)
421}
422
423//go:nosplit
424func usleep(us uint32) {
425 // Have 1us units; want 100ns units.
426 usleep1(10 * us)
427}
428
429func issigpanic(code uint32) uint32 {
430 switch code {
431 default:
432 return 0
433 case _EXCEPTION_ACCESS_VIOLATION:
434 case _EXCEPTION_INT_DIVIDE_BY_ZERO:
435 case _EXCEPTION_INT_OVERFLOW:
436 case _EXCEPTION_FLT_DENORMAL_OPERAND:
437 case _EXCEPTION_FLT_DIVIDE_BY_ZERO:
438 case _EXCEPTION_FLT_INEXACT_RESULT:
439 case _EXCEPTION_FLT_OVERFLOW:
440 case _EXCEPTION_FLT_UNDERFLOW:
441 case _EXCEPTION_BREAKPOINT:
442 }
443 return 1
444}
445
446func initsig() {
447 /*
448 // TODO(brainman): I don't think we need that bit of code
449 // following line keeps these functions alive at link stage
450 // if there's a better way please write it here
451 void *e = runtime·exceptiontramp;
452 void *f = runtime·firstcontinuetramp;
453 void *l = runtime·lastcontinuetramp;
454 USED(e);
455 USED(f);
456 USED(l);
457 */
458}
459
460func ctrlhandler1(_type uint32) uint32 {
461 var s uint32
462
463 switch _type {
464 case _CTRL_C_EVENT, _CTRL_BREAK_EVENT:
465 s = _SIGINT
466 default:
467 return 0
468 }
469
470 if sigsend(s) {
471 return 1
472 }
473 exit(2) // SIGINT, SIGTERM, etc
474 return 0
475}
476
477// in sys_windows_386.s and sys_windows_amd64.s
478func profileloop()
479
480var profiletimer uintptr
481
482func profilem(mp *m) {
483 var r *context
484 rbuf := make([]byte, unsafe.Sizeof(*r)+15)
485
486 tls := &mp.tls[0]
487 if mp == &m0 {
488 tls = &tls0[0]
489 }
490 gp := *((**g)(unsafe.Pointer(tls)))
491
492 // align Context to 16 bytes
493 r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15))
494 r.contextflags = _CONTEXT_CONTROL
495 stdcall2(_GetThreadContext, mp.thread, uintptr(unsafe.Pointer(r)))
496 dosigprof(r, gp, mp)
497}
498
499func profileloop1() {
500 stdcall2(_SetThreadPriority, currentThread, _THREAD_PRIORITY_HIGHEST)
501
502 for {
503 stdcall2(_WaitForSingleObject, profiletimer, _INFINITE)
504 first := (*m)(atomicloadp(unsafe.Pointer(&allm)))
505 for mp := first; mp != nil; mp = mp.alllink {
506 thread := atomicloaduintptr(&mp.thread)
507 // Do not profile threads blocked on Notes,
508 // this includes idle worker threads,
509 // idle timer thread, idle heap scavenger, etc.
510 if thread == 0 || mp.profilehz == 0 || mp.blocked {
511 continue
512 }
513 stdcall1(_SuspendThread, thread)
514 if mp.profilehz != 0 && !mp.blocked {
515 profilem(mp)
516 }
517 stdcall1(_ResumeThread, thread)
518 }
519 }
520}
521
522var cpuprofilerlock mutex
523
524func resetcpuprofiler(hz int32) {
525 lock(&cpuprofilerlock)
526 if profiletimer == 0 {
527 timer := stdcall3(_CreateWaitableTimerA, 0, 0, 0)
528 atomicstoreuintptr(&profiletimer, timer)
529 thread := stdcall6(_CreateThread, 0, 0, funcPC(profileloop), 0, 0, 0)
530 stdcall2(_SetThreadPriority, thread, _THREAD_PRIORITY_HIGHEST)
531 stdcall1(_CloseHandle, thread)
532 }
533 unlock(&cpuprofilerlock)
534
535 ms := int32(0)
536 due := ^int64(^uint64(1 << 63))
537 if hz > 0 {
538 ms = 1000 / hz
539 if ms == 0 {
540 ms = 1
541 }
542 due = int64(ms) * -10000
543 }
544 stdcall6(_SetWaitableTimer, profiletimer, uintptr(unsafe.Pointer(&due)), uintptr(ms), 0, 0, 0)
545 atomicstore((*uint32)(unsafe.Pointer(&getg().m.profilehz)), uint32(hz))
546}
547
548func memlimit() uintptr {
549 return 0
550}
551
552var (
553 badsignalmsg [100]byte
554 badsignallen int32
555)
556
557func setBadSignalMsg() {
558 const msg = "runtime: signal received on thread not created by Go.\n"
559 for i, c := range msg {
560 badsignalmsg[i] = byte(c)
561 badsignallen++
562 }
563}
564
565func crash() {
566 // TODO: This routine should do whatever is needed
567 // to make the Windows program abort/crash as it
568 // would if Go was not intercepting signals.
569 // On Unix the routine would remove the custom signal
570 // handler and then raise a signal (like SIGABRT).
571 // Something like that should happen here.
572 // It's okay to leave this empty for now: if crash returns
573 // the ordinary exit-after-panic happens.
574}