Skip to content

Commit

Permalink
runtime: handle all windows exception (second attempt)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alexbrainman authored and wheatman committed Jun 26, 2018
1 parent 4fc1453 commit 0069c80
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 76 deletions.
4 changes: 4 additions & 0 deletions src/runtime/defs_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const (
CONTEXT_FULL = C.CONTEXT_FULL

EXCEPTION_ACCESS_VIOLATION = C.STATUS_ACCESS_VIOLATION
EXCEPTION_BREAKPOINT = C.STATUS_BREAKPOINT
EXCEPTION_FLT_DENORMAL_OPERAND = C.STATUS_FLOAT_DENORMAL_OPERAND
EXCEPTION_FLT_DIVIDE_BY_ZERO = C.STATUS_FLOAT_DIVIDE_BY_ZERO
EXCEPTION_FLT_INEXACT_RESULT = C.STATUS_FLOAT_INEXACT_RESULT
Expand All @@ -59,6 +60,9 @@ const (

INFINITE = C.INFINITE
WAIT_TIMEOUT = C.WAIT_TIMEOUT

EXCEPTION_CONTINUE_EXECUTION = C.EXCEPTION_CONTINUE_EXECUTION
EXCEPTION_CONTINUE_SEARCH = C.EXCEPTION_CONTINUE_SEARCH
)

type SystemInfo C.SYSTEM_INFO
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/defs_windows_386.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum {
CONTEXT_FULL = 0x10007,

EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
EXCEPTION_BREAKPOINT = 0x80000003,
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
Expand All @@ -32,6 +33,9 @@ enum {

INFINITE = 0xffffffff,
WAIT_TIMEOUT = 0x102,

EXCEPTION_CONTINUE_EXECUTION = -0x1,
EXCEPTION_CONTINUE_SEARCH = 0x0,
};

typedef struct SystemInfo SystemInfo;
Expand Down
4 changes: 4 additions & 0 deletions src/runtime/defs_windows_amd64.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum {
CONTEXT_FULL = 0x10000b,

EXCEPTION_ACCESS_VIOLATION = 0xc0000005,
EXCEPTION_BREAKPOINT = 0x80000003,
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d,
EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e,
EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f,
Expand All @@ -32,6 +33,9 @@ enum {

INFINITE = 0xffffffff,
WAIT_TIMEOUT = 0x102,

EXCEPTION_CONTINUE_EXECUTION = -0x1,
EXCEPTION_CONTINUE_SEARCH = 0x0,
};

typedef struct SystemInfo SystemInfo;
Expand Down
38 changes: 32 additions & 6 deletions src/runtime/os_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll"
#pragma dynimport runtime·SetProcessPriorityBoost SetProcessPriorityBoost "kernel32.dll"
#pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll"
#pragma dynimport runtime·SetUnhandledExceptionFilter SetUnhandledExceptionFilter "kernel32.dll"
#pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll"
#pragma dynimport runtime·Sleep Sleep "kernel32.dll"
#pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll"
Expand Down Expand Up @@ -65,6 +66,7 @@ extern void *runtime·SetConsoleCtrlHandler;
extern void *runtime·SetEvent;
extern void *runtime·SetProcessPriorityBoost;
extern void *runtime·SetThreadPriority;
extern void *runtime·SetUnhandledExceptionFilter;
extern void *runtime·SetWaitableTimer;
extern void *runtime·Sleep;
extern void *runtime·SuspendThread;
Expand All @@ -77,7 +79,9 @@ void *runtime·GetQueuedCompletionStatusEx;

extern uintptr runtime·externalthreadhandlerp;
void runtime·externalthreadhandler(void);
void runtime·sigtramp(void);
void runtime·exceptiontramp(void);
void runtime·firstcontinuetramp(void);
void runtime·lastcontinuetramp(void);

#pragma textflag NOSPLIT
uintptr
Expand Down Expand Up @@ -106,12 +110,30 @@ void
runtime·osinit(void)
{
void *kernel32;
void *addVectoredContinueHandler;

kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");

runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler;

runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·sigtramp);
runtime·stdcall2(runtime·AddVectoredExceptionHandler, 1, (uintptr)runtime·exceptiontramp);
addVectoredContinueHandler = nil;
if(kernel32 != nil)
addVectoredContinueHandler = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"AddVectoredContinueHandler");
if(addVectoredContinueHandler == nil || sizeof(void*) == 4) {
// use SetUnhandledExceptionFilter for windows-386 or
// if VectoredContinueHandler is unavailable.
// note: SetUnhandledExceptionFilter handler won't be called, if debugging.
runtime·stdcall1(runtime·SetUnhandledExceptionFilter, (uintptr)runtime·lastcontinuetramp);
} else {
runtime·stdcall2(addVectoredContinueHandler, 1, (uintptr)runtime·firstcontinuetramp);
runtime·stdcall2(addVectoredContinueHandler, 0, (uintptr)runtime·lastcontinuetramp);
}

runtime·stdcall2(runtime·SetConsoleCtrlHandler, (uintptr)runtime·ctrlhandler, 1);

runtime·stdcall1(runtime·timeBeginPeriod, 1);

runtime·ncpu = getproccount();

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

kernel32 = runtime·stdcall1(runtime·LoadLibraryA, (uintptr)"kernel32.dll");
if(kernel32 != nil) {
runtime·GetQueuedCompletionStatusEx = runtime·stdcall2(runtime·GetProcAddress, (uintptr)kernel32, (uintptr)"GetQueuedCompletionStatusEx");
}
Expand Down Expand Up @@ -467,6 +488,7 @@ runtime·issigpanic(uint32 code)
case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_FLT_UNDERFLOW:
case EXCEPTION_BREAKPOINT:
return 1;
}
return 0;
Expand All @@ -475,10 +497,14 @@ runtime·issigpanic(uint32 code)
void
runtime·initsig(void)
{
// following line keeps sigtramp alive at link stage
// following line keeps these functions alive at link stage
// if there's a better way please write it here
void *p = runtime·sigtramp;
USED(p);
void *e = runtime·exceptiontramp;
void *f = runtime·firstcontinuetramp;
void *l = runtime·lastcontinuetramp;
USED(e);
USED(f);
USED(l);
}

uint32
Expand Down
84 changes: 51 additions & 33 deletions src/runtime/os_windows_386.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,63 @@ runtime·dumpregs(Context *r)
runtime·printf("gs %x\n", r->SegGs);
}

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

// Only handle exception if executing instructions in Go binary
// (not Windows library code).
if(r->Eip < (uint32)runtime·text || (uint32)runtime·etext < r->Eip)
return 0;

if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
// augmenting the stack frame would break
// the unwinding code.
gp->sig = info->ExceptionCode;
gp->sigcode0 = info->ExceptionInformation[0];
gp->sigcode1 = info->ExceptionInformation[1];
gp->sigpc = r->Eip;

// Only push runtime·sigpanic if r->eip != 0.
// If r->eip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
// (Otherwise the trace will end at runtime·sigpanic and we
// won't get to see who faulted.)
if(r->Eip != 0) {
sp = (uintptr*)r->Esp;
*--sp = r->Eip;
r->Esp = (uintptr)sp;
}
r->Eip = (uintptr)runtime·sigpanic;
return -1;
return false;

if(!runtime·issigpanic(info->ExceptionCode))
return false;

return true;
}

// Called by sigtramp from Windows VEH handler.
// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
uint32
runtime·exceptionhandler(ExceptionRecord *info, Context *r, G *gp)
{
uintptr *sp;

if(!runtime·isgoexception(info, r))
return EXCEPTION_CONTINUE_SEARCH;

// Make it look like a call to the signal func.
// Have to pass arguments out of band since
// augmenting the stack frame would break
// the unwinding code.
gp->sig = info->ExceptionCode;
gp->sigcode0 = info->ExceptionInformation[0];
gp->sigcode1 = info->ExceptionInformation[1];
gp->sigpc = r->Eip;

// Only push runtime·sigpanic if r->eip != 0.
// If r->eip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
// (Otherwise the trace will end at runtime·sigpanic and we
// won't get to see who faulted.)
if(r->Eip != 0) {
sp = (uintptr*)r->Esp;
*--sp = r->Eip;
r->Esp = (uintptr)sp;
}
r->Eip = (uintptr)runtime·sigpanic;
return EXCEPTION_CONTINUE_EXECUTION;
}

// lastcontinuehandler is reached, because runtime cannot handle
// current exception. lastcontinuehandler will print crash info and exit.
uint32
runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
{
bool crash;

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

runtime·exit(2);
return -1; // not reached
return 0; // not reached
}

void
Expand Down
97 changes: 64 additions & 33 deletions src/runtime/os_windows_amd64.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,45 +32,76 @@ runtime·dumpregs(Context *r)
runtime·printf("gs %X\n", (uint64)r->SegGs);
}

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

// Only handle exception if executing instructions in Go binary
// (not Windows library code).
if(r->Rip < (uint64)runtime·text || (uint64)runtime·etext < r->Rip)
return 0;

if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
// Make it look like a call to the signal func.
// Have to pass arguments out of band since
// augmenting the stack frame would break
// the unwinding code.
gp->sig = info->ExceptionCode;
gp->sigcode0 = info->ExceptionInformation[0];
gp->sigcode1 = info->ExceptionInformation[1];
gp->sigpc = r->Rip;

// Only push runtime·sigpanic if r->rip != 0.
// If r->rip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
// (Otherwise the trace will end at runtime·sigpanic and we
// won't get to see who faulted.)
if(r->Rip != 0) {
sp = (uintptr*)r->Rsp;
*--sp = r->Rip;
r->Rsp = (uintptr)sp;
}
r->Rip = (uintptr)runtime·sigpanic;
return -1;
return false;

if(!runtime·issigpanic(info->ExceptionCode))
return false;

return true;
}

// Called by sigtramp from Windows VEH handler.
// Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
// or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
uint32
runtime·exceptionhandler(ExceptionRecord *info, Context *r, G *gp)
{
uintptr *sp;

if(!runtime·isgoexception(info, r))
return EXCEPTION_CONTINUE_SEARCH;

// Make it look like a call to the signal func.
// Have to pass arguments out of band since
// augmenting the stack frame would break
// the unwinding code.
gp->sig = info->ExceptionCode;
gp->sigcode0 = info->ExceptionInformation[0];
gp->sigcode1 = info->ExceptionInformation[1];
gp->sigpc = r->Rip;

// Only push runtime·sigpanic if r->rip != 0.
// If r->rip == 0, probably panicked because of a
// call to a nil func. Not pushing that onto sp will
// make the trace look like a call to runtime·sigpanic instead.
// (Otherwise the trace will end at runtime·sigpanic and we
// won't get to see who faulted.)
if(r->Rip != 0) {
sp = (uintptr*)r->Rsp;
*--sp = r->Rip;
r->Rsp = (uintptr)sp;
}
r->Rip = (uintptr)runtime·sigpanic;
return EXCEPTION_CONTINUE_EXECUTION;
}

// It seems Windows searches ContinueHandler's list even
// if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION.
// firstcontinuehandler will stop that search,
// if exceptionhandler did the same earlier.
uint32
runtime·firstcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
{
USED(gp);
if(!runtime·isgoexception(info, r))
return EXCEPTION_CONTINUE_SEARCH;
return EXCEPTION_CONTINUE_EXECUTION;
}

// lastcontinuehandler is reached, because runtime cannot handle
// current exception. lastcontinuehandler will print crash info and exit.
uint32
runtime·lastcontinuehandler(ExceptionRecord *info, Context *r, G *gp)
{
bool crash;

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

runtime·exit(2);
return -1; // not reached
return 0; // not reached
}

void
Expand Down
Loading

0 comments on commit 0069c80

Please sign in to comment.