// Save current sp in m->g0->sched.sp in preparation for
// switch back to m->curg stack.
// NOTE: unwindm knows that the saved g->sched.sp is at 0(SP).
- // On Windows, the SEH is at 4(SP) and 8(SP).
MOVL m_g0(BP), SI
MOVL (g_sched+gobuf_sp)(SI), AX
MOVL AX, 0(SP)
runtime·cgocall(void (*fn)(void*), void *arg)
{
Defer d;
- SEHUnwind sehunwind;
if(!runtime·iscgo && !Solaris && !Windows)
runtime·throw("cgocall unavailable");
d.special = true;
g->defer = &d;
- // Record current SEH for restoration during endcgo.
- // This matters most when the execution stops due to panic
- // and the called C code isn't given a chance to clean up
- // the SEHs it has pushed.
- sehunwind.seh = runtime·getseh();
- sehunwind.link = m->sehunwind;
- m->sehunwind = &sehunwind;
-
m->ncgo++;
/*
m->cgomal = nil;
}
- runtime·setseh(m->sehunwind->seh);
- m->sehunwind = m->sehunwind->link;
-
if(raceenabled)
runtime·raceacquire(&cgosync);
}
func gogoBytes() int32
var GogoBytes = gogoBytes
-
-func getseh_go() uintptr
-
-var GetSEH = getseh_go
#include "os_GOOS.h"
#include "../../cmd/ld/textflag.h"
+#pragma dynimport runtime·AddVectoredExceptionHandler AddVectoredExceptionHandler "kernel32.dll"
#pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll"
#pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll"
#pragma dynimport runtime·CreateThread CreateThread "kernel32.dll"
#pragma dynimport runtime·FreeEnvironmentStringsW FreeEnvironmentStringsW "kernel32.dll"
#pragma dynimport runtime·GetEnvironmentStringsW GetEnvironmentStringsW "kernel32.dll"
#pragma dynimport runtime·GetProcAddress GetProcAddress "kernel32.dll"
+#pragma dynimport runtime·GetQueuedCompletionStatusEx GetQueuedCompletionStatusEx "kernel32.dll"
#pragma dynimport runtime·GetStdHandle GetStdHandle "kernel32.dll"
#pragma dynimport runtime·GetSystemInfo GetSystemInfo "kernel32.dll"
#pragma dynimport runtime·GetSystemTimeAsFileTime GetSystemTimeAsFileTime "kernel32.dll"
#pragma dynimport runtime·GetThreadContext GetThreadContext "kernel32.dll"
#pragma dynimport runtime·LoadLibrary LoadLibraryW "kernel32.dll"
#pragma dynimport runtime·LoadLibraryA LoadLibraryA "kernel32.dll"
+#pragma dynimport runtime·NtWaitForSingleObject NtWaitForSingleObject "ntdll.dll"
#pragma dynimport runtime·ResumeThread ResumeThread "kernel32.dll"
#pragma dynimport runtime·SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll"
#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll"
+#pragma dynimport runtime·SetProcessPriorityBoost SetProcessPriorityBoost "kernel32.dll"
#pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll"
#pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll"
#pragma dynimport runtime·Sleep Sleep "kernel32.dll"
#pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll"
-#pragma dynimport runtime·timeBeginPeriod timeBeginPeriod "winmm.dll"
#pragma dynimport runtime·WaitForSingleObject WaitForSingleObject "kernel32.dll"
#pragma dynimport runtime·WriteFile WriteFile "kernel32.dll"
-#pragma dynimport runtime·NtWaitForSingleObject NtWaitForSingleObject "ntdll.dll"
-
-extern void *runtime·NtWaitForSingleObject;
+#pragma dynimport runtime·timeBeginPeriod timeBeginPeriod "winmm.dll"
+extern void *runtime·AddVectoredExceptionHandler;
extern void *runtime·CloseHandle;
extern void *runtime·CreateEvent;
extern void *runtime·CreateThread;
extern void *runtime·FreeEnvironmentStringsW;
extern void *runtime·GetEnvironmentStringsW;
extern void *runtime·GetProcAddress;
+extern void *runtime·GetQueuedCompletionStatusEx;
extern void *runtime·GetStdHandle;
extern void *runtime·GetSystemInfo;
extern void *runtime·GetSystemTimeAsFileTime;
extern void *runtime·GetThreadContext;
extern void *runtime·LoadLibrary;
extern void *runtime·LoadLibraryA;
+extern void *runtime·NtWaitForSingleObject;
extern void *runtime·ResumeThread;
extern void *runtime·SetConsoleCtrlHandler;
extern void *runtime·SetEvent;
+extern void *runtime·SetProcessPriorityBoost;
extern void *runtime·SetThreadPriority;
extern void *runtime·SetWaitableTimer;
extern void *runtime·Sleep;
extern void *runtime·SuspendThread;
-extern void *runtime·timeBeginPeriod;
extern void *runtime·WaitForSingleObject;
extern void *runtime·WriteFile;
-
-void *runtime·GetQueuedCompletionStatusEx;
+extern void *runtime·timeBeginPeriod;
extern uintptr runtime·externalthreadhandlerp;
void runtime·externalthreadhandler(void);
+void runtime·sigtramp(void);
static int32
getproccount(void)
void
runtime·osinit(void)
{
- void *kernel32;
- void *SetProcessPriorityBoost;
-
runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler;
+ runtime·stdcall(runtime·AddVectoredExceptionHandler, 2, (uintptr)1, (uintptr)runtime·sigtramp);
runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1);
runtime·stdcall(runtime·timeBeginPeriod, 1, (uintptr)1);
runtime·ncpu = getproccount();
-
- kernel32 = runtime·stdcall(runtime·LoadLibraryA, 1, "kernel32.dll");
- if(kernel32 != nil) {
- // Windows dynamic priority boosting assumes that a process has different types
- // of dedicated threads -- GUI, IO, computational, etc. Go processes use
- // equivalent threads that all do a mix of GUI, IO, computations, etc.
- // In such context dynamic priority boosting does nothing but harm, so we turn it off.
- SetProcessPriorityBoost = runtime·stdcall(runtime·GetProcAddress, 2, kernel32, "SetProcessPriorityBoost");
- if(SetProcessPriorityBoost != nil) // supported since Windows XP
- runtime·stdcall(SetProcessPriorityBoost, 2, (uintptr)-1, (uintptr)1);
- runtime·GetQueuedCompletionStatusEx = runtime·stdcall(runtime·GetProcAddress, 2, kernel32, "GetQueuedCompletionStatusEx");
- }
+
+ // Windows dynamic priority boosting assumes that a process has different types
+ // of dedicated threads -- GUI, IO, computational, etc. Go processes use
+ // equivalent threads that all do a mix of GUI, IO, computations, etc.
+ // In such context dynamic priority boosting does nothing but harm, so we turn it off.
+ runtime·stdcall(runtime·SetProcessPriorityBoost, 2, (uintptr)-1, (uintptr)1);
}
void
runtime·throw("fault");
}
-extern void *runtime·sigtramp;
-
void
runtime·initsig(void)
{
runtime·printf("gs %x\n", r->SegGs);
}
+#define DBG_PRINTEXCEPTION_C 0x40010006
+
+// 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 crash;
uintptr *sp;
- /*
switch(info->ExceptionCode) {
+ case DBG_PRINTEXCEPTION_C:
+ // This exception is intended to be caught by debuggers.
+ // There is a not-very-informational message like
+ // "Invalid parameter passed to C runtime function"
+ // sitting at info->ExceptionInformation[0] (a wchar_t*),
+ // with length info->ExceptionInformation[1].
+ // The default behavior is to ignore this exception,
+ // but somehow returning 0 here (meaning keep going)
+ // makes the program crash instead. Maybe Windows has no
+ // other handler registered? In any event, ignore it.
+ return -1;
+
case EXCEPTION_BREAKPOINT:
- r->Eip--; // because 8l generates 2 bytes for INT3
- return 1;
+ // It is unclear whether this is needed, unclear whether it
+ // would work, and unclear how to test it. Leave out for now.
+ // This only handles breakpoint instructions written in the
+ // assembly sources, not breakpoints set by a debugger, and
+ // there are very few of the former.
+ //
+ // r->Eip--; // because 8l generates 2 bytes for INT3
+ // return 0;
+ break;
}
- */
if(gp != nil && runtime·issigpanic(info->ExceptionCode)) {
// Make it look like a call to the signal func.
r->Esp = (uintptr)sp;
}
r->Eip = (uintptr)runtime·sigpanic;
- return 0;
+ return -1;
}
if(runtime·panicking) // traceback already printed
runtime·exit(2);
runtime·panicking = 1;
- runtime·printf("Exception %x %p %p\n", info->ExceptionCode,
- info->ExceptionInformation[0], info->ExceptionInformation[1]);
+ runtime·printf("Exception %x %p %p %p\n", info->ExceptionCode,
+ info->ExceptionInformation[0], info->ExceptionInformation[1], r->Eip);
runtime·printf("PC=%x\n", r->Eip);
if(m->lockedg != nil && m->ncgo > 0 && gp == m->g0) {
if(crash)
runtime·crash();
-
runtime·exit(2);
- return 0;
+ return -1; // not reached
}
void
void
runtime·mstart(void)
{
-#ifdef GOOSARCH_windows_386
- // It is used by windows-386 only. Unfortunately, seh needs
- // to be located on os stack, and mstart runs on os stack
- // for both m0 and m.
- SEH seh;
-#endif
-
if(g != m->g0)
runtime·throw("bad runtime·mstart");
runtime·gosave(&m->g0->sched);
m->g0->sched.pc = (uintptr)-1; // make sure it is never used
m->g0->stackguard = m->g0->stackguard0; // cgo sets only stackguard0, copy it to stackguard
-#ifdef GOOSARCH_windows_386
- m->seh = &seh;
-#endif
runtime·asminit();
runtime·minit();
g->stackguard = (uintptr)(&x - 32*1024);
g->stackguard0 = g->stackguard;
-#ifdef GOOSARCH_windows_386
- // On windows/386, we need to put an SEH frame (two words)
- // somewhere on the current stack. We are called from cgocallback_gofunc
- // and we know that it will leave two unused words below m->curg->sched.sp.
- // Use those.
- m->seh = (SEH*)((uintptr*)&x + 1);
-#endif
-
// Initialize this thread to use the m.
runtime·asminit();
runtime·minit();
// Undo whatever initialization minit did during needm.
runtime·unminit();
-#ifdef GOOSARCH_windows_386
- m->seh = nil; // reset dangling typed pointer
-#endif
-
// Clear m and g, and return m to the extra list.
// After the call to setmg we can only call nosplit functions.
mp = m;
typedef struct Complex64 Complex64;
typedef struct Complex128 Complex128;
typedef struct LibCall LibCall;
-typedef struct SEH SEH;
-typedef struct SEHUnwind SEHUnwind;
typedef struct WinCallbackContext WinCallbackContext;
typedef struct Timers Timers;
typedef struct Timer Timer;
uintptr err; // error number
};
-struct SEH
-{
- void* prev;
- void* handler;
-};
-
-struct SEHUnwind
-{
- SEHUnwind* link;
- SEH* seh;
-};
-
// describes how to handle callback
struct WinCallbackContext
{
uintptr end[];
};
-// Define a symbol for windows/386 because that is the only
-// system with SEH handling, and we end up checking that
-// repeatedly.
-#ifdef GOOS_windows
-#ifdef GOARCH_386
-#define GOOSARCH_windows_386
-#endif
-#endif
-
struct M
{
G* g0; // goroutine with scheduling stack
int8* notesig;
byte* errstr;
#endif
- SEH* seh;
- SEHUnwind* sehunwind;
uintptr end[];
};
int32 runtime·setmaxthreads(int32);
G* runtime·timejump(void);
-// On Windows 386, we have functions for saving and restoring
-// the SEH values; elsewhere #define them away.
-#ifdef GOOSARCH_windows_386
-SEH* runtime·getseh(void);
-void runtime·setseh(SEH*);
-#else
-#define runtime·getseh() nil
-#define runtime·setseh(x) do{}while(0)
-#endif
-
#pragma varargck argpos runtime·printf 1
#pragma varargck type "c" int32
#pragma varargck type "d" int32
x = RuntimeGogoBytes;
}
-func getseh_go() (x uintptr) {
- x = (uintptr)runtime·getseh();
-}
-
func typestring(e Eface) (s String) {
s = *e.type->string;
}
MOVL AX, 0x34(FS)
RET
-TEXT runtime·sigtramp(SB),NOSPLIT,$28
- // unwinding?
- MOVL info+0(FP), CX
- TESTL $6, 4(CX) // exception flags
- MOVL $1, AX
- JNZ sigdone
-
- // copy arguments for call to sighandler
- MOVL CX, 0(SP)
- MOVL context+8(FP), CX
- MOVL CX, 4(SP)
-
- get_tls(CX)
-
- // check that m exists
- MOVL m(CX), AX
- CMPL AX, $0
- JNE 2(PC)
- CALL runtime·badsignal2(SB)
-
- MOVL g(CX), CX
- MOVL CX, 8(SP)
-
+// Called by Windows as a Vectored Exception Handler (VEH).
+// First argument is pointer to struct containing
+// exception record and context pointers.
+// Return 0 for 'not handled', -1 for handled.
+TEXT runtime·sigtramp(SB),NOSPLIT,$0-0
+ MOVL ptrs+0(FP), DI
+ SUBL $28, SP
+ MOVL 0(DI), BX // ExceptionRecord*
+ MOVL 4(DI), CX // Context*
+
+ // Only handle exception if executing instructions in Go binary
+ // (not Windows library code). Except don't - keep reading.
+ //
+ // This sounds like a good idea but the tracebacks that
+ // Go provides are better than the Windows crash dialog,
+ // especially if it's something that Go needs to do.
+ // So take all the exceptions, not just the ones at Go PCs.
+ // If you re-enable this check by removing the JMP, you will
+ // need to arrange to handle exception 0x40010006 during
+ // non-Go code here. Right now that case is handled by sighandler
+ // in os_windows_386.c.
+ JMP skipcheckpc
+ MOVL $0, AX
+ MOVL 184(CX), DX // saved PC
+ CMPL DX, $text(SB)
+ JB vehret
+ CMPL DX, $etext(SB)
+ JA vehret
+
+skipcheckpc:
+ // save callee-saved registers
MOVL BX, 12(SP)
MOVL BP, 16(SP)
MOVL SI, 20(SP)
MOVL DI, 24(SP)
+ // fetch g
+ get_tls(DX)
+ MOVL m(DX), AX
+ CMPL AX, $0
+ JNE 2(PC)
+ CALL runtime·badsignal2(SB)
+ MOVL g(DX), DX
+ // call sighandler(ExceptionRecord*, Context*, G*)
+ MOVL BX, 0(SP)
+ MOVL CX, 4(SP)
+ MOVL DX, 8(SP)
CALL runtime·sighandler(SB)
- // AX is set to report result back to Windows
+ // restore callee-saved registers
MOVL 24(SP), DI
MOVL 20(SP), SI
MOVL 16(SP), BP
MOVL 12(SP), BX
-sigdone:
- RET
+
+vehret:
+ ADDL $28, SP
+ // RET 4 (return and pop 4 bytes parameters)
+ BYTE $0xC2; WORD $4
+ RET // unreached; make assembler happy
TEXT runtime·ctrlhandler(SB),NOSPLIT,$0
PUSHL $runtime·ctrlhandler1(SB)
PUSHL BP
PUSHL BX
- // set up SEH frame again
- PUSHL $runtime·sigtramp(SB)
- PUSHL 0(FS)
- MOVL SP, 0(FS)
-
// determine index into runtime·cbctxts table
SUBL $runtime·callbackasm(SB), AX
MOVL $0, DX
MOVL BX, CX // cannot use BX anymore
- // pop SEH frame
- POPL 0(FS)
- POPL BX
-
// restore registers as required for windows callback
POPL BX
POPL BP
// void install_exception_handler()
TEXT runtime·install_exception_handler(SB),NOSPLIT,$0
- get_tls(CX)
- MOVL m(CX), CX // m
-
- // Set up SEH frame
- MOVL m_seh(CX), DX
- MOVL $runtime·sigtramp(SB), AX
- MOVL AX, seh_handler(DX)
- MOVL 0(FS), AX
- MOVL AX, seh_prev(DX)
-
- // Install it
- MOVL DX, 0(FS)
-
RET
// void remove_exception_handler()
TEXT runtime·remove_exception_handler(SB),NOSPLIT,$0
- get_tls(CX)
- MOVL m(CX), CX // m
-
- // Remove SEH frame
- MOVL m_seh(CX), DX
- MOVL seh_prev(DX), AX
- MOVL AX, 0(FS)
-
RET
// Sleep duration is in 100ns units.
CALL AX
MOVL BP, SP
RET
-
-TEXT runtime·getseh(SB),NOSPLIT,$0
- MOVL 0(FS), AX
- RET
-
-TEXT runtime·setseh(SB),NOSPLIT,$0
- MOVL seh+0(FP), AX
- MOVL AX, 0(FS)
- RET
-
nestedCall(t, runtime.GC)
}
-// NOTE: TestCallbackPanicLocked must precede the other TestCallbackPanic variants.
-// The SEH logic is testing that SEH is properly restored during the panic.
-// The bug we're looking for (issue 7470) used to leave SEH in the wrong place,
-// but future panics would leave it in that same wrong place. So if one of the other
-// tests runs first, TestCallbackPanicLocked will see SEH not changing and
-// incorrectly infer that it is being restored properly.
-// The SEH checks are only safe (not racy) with the OS thread locked.
-//
-// The fallback is that even if this test doesn't notice, TestSetPanicOnFault will
-// crash if it runs on the same thread after one of these tests.
-
func TestCallbackPanicLocked(t *testing.T) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if !runtime.LockedOSThread() {
t.Fatal("runtime.LockOSThread didn't")
}
- oldSEH := runtime.GetSEH()
defer func() {
s := recover()
if s == nil {
if !runtime.LockedOSThread() {
t.Fatal("lost lock on OS thread after panic")
}
- if newSEH := runtime.GetSEH(); oldSEH != newSEH {
- t.Fatalf("SEH not restored after panic: %#x became %#x", oldSEH, newSEH)
- }
}()
nestedCall(t, func() { panic("callback panic") })
panic("nestedCall returned")