Skip to content

Commit 0069c80

Browse files
alexbrainmanwheatman
authored andcommitted
runtime: handle all windows exception (second attempt)
includes undo of 22318cd31d7d and also: - always use SetUnhandledExceptionFilter on windows-386; - crash when receive EXCEPTION_BREAKPOINT in exception handler. Fixes golang#8006. LGTM=rsc R=golang-codereviews, rsc CC=golang-codereviews https://golang.org/cl/155360043
1 parent 4fc1453 commit 0069c80

9 files changed

+230
-76
lines changed

src/runtime/defs_windows.go

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const (
4949
CONTEXT_FULL = C.CONTEXT_FULL
5050

5151
EXCEPTION_ACCESS_VIOLATION = C.STATUS_ACCESS_VIOLATION
52+
EXCEPTION_BREAKPOINT = C.STATUS_BREAKPOINT
5253
EXCEPTION_FLT_DENORMAL_OPERAND = C.STATUS_FLOAT_DENORMAL_OPERAND
5354
EXCEPTION_FLT_DIVIDE_BY_ZERO = C.STATUS_FLOAT_DIVIDE_BY_ZERO
5455
EXCEPTION_FLT_INEXACT_RESULT = C.STATUS_FLOAT_INEXACT_RESULT
@@ -59,6 +60,9 @@ const (
5960

6061
INFINITE = C.INFINITE
6162
WAIT_TIMEOUT = C.WAIT_TIMEOUT
63+
64+
EXCEPTION_CONTINUE_EXECUTION = C.EXCEPTION_CONTINUE_EXECUTION
65+
EXCEPTION_CONTINUE_SEARCH = C.EXCEPTION_CONTINUE_SEARCH
6266
)
6367

6468
type SystemInfo C.SYSTEM_INFO

src/runtime/defs_windows_386.h

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum {
2222
CONTEXT_FULL = 0x10007,
2323

2424
EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
25+
EXCEPTION_BREAKPOINT = 0x80000003,
2526
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
2627
EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
2728
EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
@@ -32,6 +33,9 @@ enum {
3233

3334
INFINITE = 0xffffffff,
3435
WAIT_TIMEOUT = 0x102,
36+
37+
EXCEPTION_CONTINUE_EXECUTION = -0x1,
38+
EXCEPTION_CONTINUE_SEARCH = 0x0,
3539
};
3640

3741
typedef struct SystemInfo SystemInfo;

src/runtime/defs_windows_amd64.h

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum {
2222
CONTEXT_FULL = 0x10000b,
2323

2424
EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
25+
EXCEPTION_BREAKPOINT = 0x80000003,
2526
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
2627
EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
2728
EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
@@ -32,6 +33,9 @@ enum {
3233

3334
INFINITE = 0xffffffff,
3435
WAIT_TIMEOUT = 0x102,
36+
37+
EXCEPTION_CONTINUE_EXECUTION = -0x1,
38+
EXCEPTION_CONTINUE_SEARCH = 0x0,
3539
};
3640

3741
typedef struct SystemInfo SystemInfo;

src/runtime/os_windows.c

+32-6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll"
3535
#pragma dynimport runtime·SetProcessPriorityBoost SetProcessPriorityBoost "kernel32.dll"
3636
#pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll"
37+
#pragma dynimport runtime·SetUnhandledExceptionFilter SetUnhandledExceptionFilter "kernel32.dll"
3738
#pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll"
3839
#pragma dynimport runtime·Sleep Sleep "kernel32.dll"
3940
#pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll"
@@ -65,6 +66,7 @@ extern void *runtime·SetConsoleCtrlHandler;
6566
extern void *runtime·SetEvent;
6667
extern void *runtime·SetProcessPriorityBoost;
6768
extern void *runtime·SetThreadPriority;
69+
extern void *runtime·SetUnhandledExceptionFilter;
6870
extern void *runtime·SetWaitableTimer;
6971
extern void *runtime·Sleep;
7072
extern void *runtime·SuspendThread;
@@ -77,7 +79,9 @@ void *runtime·GetQueuedCompletionStatusEx;
7779

7880
extern uintptr runtime·externalthreadhandlerp;
7981
void runtime·externalthreadhandler(void);
80-
void runtime·sigtramp(void);
82+
void runtime·exceptiontramp(void);
83+
void runtime·firstcontinuetramp(void);
84+
void runtime·lastcontinuetramp(void);
8185

8286
#pragma textflag NOSPLIT
8387
uintptr
@@ -106,12 +110,30 @@ void
106110
runtime·osinit(void)
107111
{
108112
void *kernel32;
113+
void *addVectoredContinueHandler;
114+
115+
kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
109116

110117
runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler;
111118

112-
runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·sigtramp);
119+
runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·exceptiontramp);
120+
addVectoredContinueHandler = nil;
121+
if(kernel32 != nil)
122+
addVectoredContinueHandler = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"AddVectoredContinueHandler");
123+
if(addVectoredContinueHandler == nil || sizeof(void*) == 4) {
124+
// use SetUnhandledExceptionFilter for windows-386 or
125+
// if VectoredContinueHandler is unavailable.
126+
// note: SetUnhandledExceptionFilter handler won't be called, if debugging.
127+
runtime·stdcall1(runtime·SetUnhandledExceptionFilter, (uintptr)runtime·lastcontinuetramp);
128+
} else {
129+
runtime·stdcall2(addVectoredContinueHandler, 1, (uintptr)runtime·firstcontinuetramp);
130+
runtime·stdcall2(addVectoredContinueHandler, 0, (uintptr)runtime·lastcontinuetramp);
131+
}
132+
113133
runtime·stdcall2(runtime·SetConsoleCtrlHandler, (uintptr)runtime·ctrlhandler, 1);
134+
114135
runtime·stdcall1(runtime·timeBeginPeriod, 1);
136+
115137
runtime·ncpu = getproccount();
116138

117139
// Windows dynamic priority boosting assumes that a process has different types
@@ -120,7 +142,6 @@ runtime·osinit(void)
120142
// In such context dynamic priority boosting does nothing but harm, so we turn it off.
121143
runtime·stdcall2(runtime·SetProcessPriorityBoost, -1, 1);
122144

123-
kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
124145
if(kernel32 != nil) {
125146
runtime·GetQueuedCompletionStatusEx = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"GetQueuedCompletionStatusEx");
126147
}
@@ -467,6 +488,7 @@ runtime·issigpanic(uint32 code)
467488
case EXCEPTION_FLT_INEXACT_RESULT:
468489
case EXCEPTION_FLT_OVERFLOW:
469490
case EXCEPTION_FLT_UNDERFLOW:
491+
case EXCEPTION_BREAKPOINT:
470492
return 1;
471493
}
472494
return 0;
@@ -475,10 +497,14 @@ runtime·issigpanic(uint32 code)
475497
void
476498
runtime·initsig(void)
477499
{
478-
// following line keeps sigtramp alive at link stage
500+
// following line keeps these functions alive at link stage
479501
// if there's a better way please write it here
480-
void *p = runtime·sigtramp;
481-
USED(p);
502+
void *e = runtime·exceptiontramp;
503+
void *f = runtime·firstcontinuetramp;
504+
void *l = runtime·lastcontinuetramp;
505+
USED(e);
506+
USED(f);
507+
USED(l);
482508
}
483509

484510
uint32

src/runtime/os_windows_386.c

+51-33
Original file line numberDiff line numberDiff line change
@@ -24,45 +24,63 @@ runtime·dumpregs(Context *r)
2424
runtime·printf("gs %x\n", r->SegGs);
2525
}
2626

27-
// Called by sigtramp from Windows VEH handler.
28-
// Return value signals whether the exception has been handled (-1)
29-
// or should be made available to other handlers in the chain (0).
30-
uint32
31-
runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
27+
bool
28+
runtime·isgoexception(ExceptionRecord *info, Context *r)
3229
{
33-
bool crash;
34-
uintptr *sp;
3530
extern byte runtime·text[], runtime·etext[];
3631

3732
// Only handle exception if executing instructions in Go binary
3833
// (not Windows library code).
3934
if(r->Eip < (uint32)runtime·text || (uint32)runtime·etext < r->Eip)
40-
return 0;
41-
42-
if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
43-
// Make it look like a call to the signal func.
44-
// Have to pass arguments out of band since
45-
// augmenting the stack frame would break
46-
// the unwinding code.
47-
gp->sig = info->ExceptionCode;
48-
gp->sigcode0 = info->ExceptionInformation[0];
49-
gp->sigcode1 = info->ExceptionInformation[1];
50-
gp->sigpc = r->Eip;
51-
52-
// Only push runtime·sigpanic if r->eip != 0.
53-
// If r->eip == 0, probably panicked because of a
54-
// call to a nil func. Not pushing that onto sp will
55-
// make the trace look like a call to runtime·sigpanic instead.
56-
// (Otherwise the trace will end at runtime·sigpanic and we
57-
// won't get to see who faulted.)
58-
if(r->Eip != 0) {
59-
sp = (uintptr*)r->Esp;
60-
*--sp = r->Eip;
61-
r->Esp = (uintptr)sp;
62-
}
63-
r->Eip = (uintptr)runtime·sigpanic;
64-
return -1;
35+
return false;
36+
37+
if(!runtime·issigpanic(info->ExceptionCode))
38+
return false;
39+
40+
return true;
41+
}
42+
43+
// Called by sigtramp from Windows VEH handler.
44+
// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
45+
// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
46+
uint32
47+
runtime·exceptionhandler(ExceptionRecord *info, Context *r, G *gp)
48+
{
49+
uintptr *sp;
50+
51+
if(!runtime·isgoexception(info, r))
52+
return EXCEPTION_CONTINUE_SEARCH;
53+
54+
// Make it look like a call to the signal func.
55+
// Have to pass arguments out of band since
56+
// augmenting the stack frame would break
57+
// the unwinding code.
58+
gp->sig = info->ExceptionCode;
59+
gp->sigcode0 = info->ExceptionInformation[0];
60+
gp->sigcode1 = info->ExceptionInformation[1];
61+
gp->sigpc = r->Eip;
62+
63+
// Only push runtime·sigpanic if r->eip != 0.
64+
// If r->eip == 0, probably panicked because of a
65+
// call to a nil func. Not pushing that onto sp will
66+
// make the trace look like a call to runtime·sigpanic instead.
67+
// (Otherwise the trace will end at runtime·sigpanic and we
68+
// won't get to see who faulted.)
69+
if(r->Eip != 0) {
70+
sp = (uintptr*)r->Esp;
71+
*--sp = r->Eip;
72+
r->Esp = (uintptr)sp;
6573
}
74+
r->Eip = (uintptr)runtime·sigpanic;
75+
return EXCEPTION_CONTINUE_EXECUTION;
76+
}
77+
78+
// lastcontinuehandler is reached, because runtime cannot handle
79+
// current exception. lastcontinuehandler will print crash info and exit.
80+
uint32
81+
runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
82+
{
83+
bool crash;
6684

6785
if(runtime·panicking) // traceback already printed
6886
runtime·exit(2);
@@ -88,7 +106,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
88106
runtime·crash();
89107

90108
runtime·exit(2);
91-
return -1; // not reached
109+
return 0; // not reached
92110
}
93111

94112
void

src/runtime/os_windows_amd64.c

+64-33
Original file line numberDiff line numberDiff line change
@@ -32,45 +32,76 @@ runtime·dumpregs(Context *r)
3232
runtime·printf("gs %X\n", (uint64)r->SegGs);
3333
}
3434

35-
// Called by sigtramp from Windows VEH handler.
36-
// Return value signals whether the exception has been handled (-1)
37-
// or should be made available to other handlers in the chain (0).
38-
uint32
39-
runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
35+
bool
36+
runtime·isgoexception(ExceptionRecord *info, Context *r)
4037
{
41-
bool crash;
42-
uintptr *sp;
4338
extern byte runtime·text[], runtime·etext[];
4439

4540
// Only handle exception if executing instructions in Go binary
4641
// (not Windows library code).
4742
if(r->Rip < (uint64)runtime·text || (uint64)runtime·etext < r->Rip)
48-
return 0;
49-
50-
if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
51-
// Make it look like a call to the signal func.
52-
// Have to pass arguments out of band since
53-
// augmenting the stack frame would break
54-
// the unwinding code.
55-
gp->sig = info->ExceptionCode;
56-
gp->sigcode0 = info->ExceptionInformation[0];
57-
gp->sigcode1 = info->ExceptionInformation[1];
58-
gp->sigpc = r->Rip;
59-
60-
// Only push runtime·sigpanic if r->rip != 0.
61-
// If r->rip == 0, probably panicked because of a
62-
// call to a nil func. Not pushing that onto sp will
63-
// make the trace look like a call to runtime·sigpanic instead.
64-
// (Otherwise the trace will end at runtime·sigpanic and we
65-
// won't get to see who faulted.)
66-
if(r->Rip != 0) {
67-
sp = (uintptr*)r->Rsp;
68-
*--sp = r->Rip;
69-
r->Rsp = (uintptr)sp;
70-
}
71-
r->Rip = (uintptr)runtime·sigpanic;
72-
return -1;
43+
return false;
44+
45+
if(!runtime·issigpanic(info->ExceptionCode))
46+
return false;
47+
48+
return true;
49+
}
50+
51+
// Called by sigtramp from Windows VEH handler.
52+
// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
53+
// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
54+
uint32
55+
runtime·exceptionhandler(ExceptionRecord *info, Context *r, G *gp)
56+
{
57+
uintptr *sp;
58+
59+
if(!runtime·isgoexception(info, r))
60+
return EXCEPTION_CONTINUE_SEARCH;
61+
62+
// Make it look like a call to the signal func.
63+
// Have to pass arguments out of band since
64+
// augmenting the stack frame would break
65+
// the unwinding code.
66+
gp->sig = info->ExceptionCode;
67+
gp->sigcode0 = info->ExceptionInformation[0];
68+
gp->sigcode1 = info->ExceptionInformation[1];
69+
gp->sigpc = r->Rip;
70+
71+
// Only push runtime·sigpanic if r->rip != 0.
72+
// If r->rip == 0, probably panicked because of a
73+
// call to a nil func. Not pushing that onto sp will
74+
// make the trace look like a call to runtime·sigpanic instead.
75+
// (Otherwise the trace will end at runtime·sigpanic and we
76+
// won't get to see who faulted.)
77+
if(r->Rip != 0) {
78+
sp = (uintptr*)r->Rsp;
79+
*--sp = r->Rip;
80+
r->Rsp = (uintptr)sp;
7381
}
82+
r->Rip = (uintptr)runtime·sigpanic;
83+
return EXCEPTION_CONTINUE_EXECUTION;
84+
}
85+
86+
// It seems Windows searches ContinueHandler's list even
87+
// if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION.
88+
// firstcontinuehandler will stop that search,
89+
// if exceptionhandler did the same earlier.
90+
uint32
91+
runtime·firstcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
92+
{
93+
USED(gp);
94+
if(!runtime·isgoexception(info, r))
95+
return EXCEPTION_CONTINUE_SEARCH;
96+
return EXCEPTION_CONTINUE_EXECUTION;
97+
}
98+
99+
// lastcontinuehandler is reached, because runtime cannot handle
100+
// current exception. lastcontinuehandler will print crash info and exit.
101+
uint32
102+
runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
103+
{
104+
bool crash;
74105

75106
if(runtime·panicking) // traceback already printed
76107
runtime·exit(2);
@@ -97,7 +128,7 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
97128
runtime·crash();
98129

99130
runtime·exit(2);
100-
return -1; // not reached
131+
return 0; // not reached
101132
}
102133

103134
void

0 commit comments

Comments
 (0)