mirror of
https://github.com/golang/go
synced 2024-11-11 23:10:23 -07:00
runtime: use VEH, not SEH, for windows/386 exception handling
Structured Exception Handling (SEH) was the first way to handle exceptions (memory faults, divides by zero) on Windows. The S might as well stand for "stack-based": the implementation interprets stack addresses in a few different ways, and it gets subtly confused by Go's management of stacks. It's also something that requires active maintenance during cgo switches, and we've had bugs in that maintenance in the past. We have recently come to believe that SEH cannot work with Go's stack usage. See http://golang.org/issue/7325 for details. Vectored Exception Handling (VEH) is more like a Unix signal handler: you set it once for the whole process and forget about it. This CL drops all the SEH code and replaces it with VEH code. Many special cases and 7 #ifdefs disappear. VEH was introduced in Windows XP, so Go on windows/386 will now require Windows XP or later. The previous requirement was Windows 2000 or later. Windows 2000 immediately preceded Windows XP, so Windows 2000 is the only affected version. Microsoft stopped supporting Windows 2000 in 2010. See http://golang.org/s/win2000-golang-nuts for details. Fixes #7325. LGTM=alex.brainman, r R=golang-codereviews, alex.brainman, stephen.gutekanst, dave CC=golang-codereviews, iant, r https://golang.org/cl/74790043
This commit is contained in:
parent
3b27343c14
commit
3750904a7e
@ -657,7 +657,6 @@ havem:
|
||||
// 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)
|
||||
|
@ -98,7 +98,6 @@ void
|
||||
runtime·cgocall(void (*fn)(void*), void *arg)
|
||||
{
|
||||
Defer d;
|
||||
SEHUnwind sehunwind;
|
||||
|
||||
if(!runtime·iscgo && !Solaris && !Windows)
|
||||
runtime·throw("cgocall unavailable");
|
||||
@ -127,14 +126,6 @@ runtime·cgocall(void (*fn)(void*), void *arg)
|
||||
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++;
|
||||
|
||||
/*
|
||||
@ -170,9 +161,6 @@ endcgo(void)
|
||||
m->cgomal = nil;
|
||||
}
|
||||
|
||||
runtime·setseh(m->sehunwind->seh);
|
||||
m->sehunwind = m->sehunwind->link;
|
||||
|
||||
if(raceenabled)
|
||||
runtime·raceacquire(&cgosync);
|
||||
}
|
||||
|
@ -90,7 +90,3 @@ var MemclrBytes = memclrBytes
|
||||
func gogoBytes() int32
|
||||
|
||||
var GogoBytes = gogoBytes
|
||||
|
||||
func getseh_go() uintptr
|
||||
|
||||
var GetSEH = getseh_go
|
||||
|
@ -8,6 +8,7 @@
|
||||
#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"
|
||||
@ -20,26 +21,27 @@
|
||||
#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;
|
||||
@ -52,27 +54,29 @@ extern void *runtime·ExitProcess;
|
||||
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)
|
||||
@ -86,26 +90,18 @@ 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
|
||||
@ -369,8 +365,6 @@ runtime·sigpanic(void)
|
||||
runtime·throw("fault");
|
||||
}
|
||||
|
||||
extern void *runtime·sigtramp;
|
||||
|
||||
void
|
||||
runtime·initsig(void)
|
||||
{
|
||||
|
@ -24,19 +24,41 @@ runtime·dumpregs(Context *r)
|
||||
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.
|
||||
@ -60,15 +82,15 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
|
||||
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) {
|
||||
@ -86,9 +108,8 @@ runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
|
||||
if(crash)
|
||||
runtime·crash();
|
||||
|
||||
|
||||
runtime·exit(2);
|
||||
return 0;
|
||||
return -1; // not reached
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -599,13 +599,6 @@ runtime·starttheworld(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");
|
||||
|
||||
@ -615,9 +608,6 @@ runtime·mstart(void)
|
||||
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();
|
||||
|
||||
@ -769,14 +759,6 @@ runtime·needm(byte x)
|
||||
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();
|
||||
@ -854,10 +836,6 @@ runtime·dropm(void)
|
||||
// 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;
|
||||
|
@ -83,8 +83,6 @@ typedef struct Hchan Hchan;
|
||||
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;
|
||||
@ -241,18 +239,6 @@ struct LibCall
|
||||
uintptr err; // error number
|
||||
};
|
||||
|
||||
struct SEH
|
||||
{
|
||||
void* prev;
|
||||
void* handler;
|
||||
};
|
||||
|
||||
struct SEHUnwind
|
||||
{
|
||||
SEHUnwind* link;
|
||||
SEH* seh;
|
||||
};
|
||||
|
||||
// describes how to handle callback
|
||||
struct WinCallbackContext
|
||||
{
|
||||
@ -303,15 +289,6 @@ struct G
|
||||
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
|
||||
@ -394,8 +371,6 @@ struct M
|
||||
int8* notesig;
|
||||
byte* errstr;
|
||||
#endif
|
||||
SEH* seh;
|
||||
SEHUnwind* sehunwind;
|
||||
uintptr end[];
|
||||
};
|
||||
|
||||
@ -975,16 +950,6 @@ void* runtime·funcdata(Func*, int32);
|
||||
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
|
||||
|
@ -43,10 +43,6 @@ func gogoBytes() (x int32) {
|
||||
x = RuntimeGogoBytes;
|
||||
}
|
||||
|
||||
func getseh_go() (x uintptr) {
|
||||
x = (uintptr)runtime·getseh();
|
||||
}
|
||||
|
||||
func typestring(e Eface) (s String) {
|
||||
s = *e.type->string;
|
||||
}
|
||||
|
@ -69,43 +69,66 @@ TEXT runtime·setlasterror(SB),NOSPLIT,$0
|
||||
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
|
||||
// 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*
|
||||
|
||||
// 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)
|
||||
// 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)
|
||||
@ -182,11 +205,6 @@ TEXT runtime·callbackasm1+0(SB),NOSPLIT,$0
|
||||
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
|
||||
@ -232,10 +250,6 @@ TEXT runtime·callbackasm1+0(SB),NOSPLIT,$0
|
||||
|
||||
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
|
||||
@ -301,31 +315,10 @@ TEXT runtime·setldt(SB),NOSPLIT,$0
|
||||
|
||||
// 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.
|
||||
@ -390,13 +383,3 @@ TEXT runtime·usleep2(SB),NOSPLIT,$20
|
||||
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
|
||||
|
||||
|
@ -177,17 +177,6 @@ func TestCallbackGC(t *testing.T) {
|
||||
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()
|
||||
@ -195,7 +184,6 @@ func TestCallbackPanicLocked(t *testing.T) {
|
||||
if !runtime.LockedOSThread() {
|
||||
t.Fatal("runtime.LockOSThread didn't")
|
||||
}
|
||||
oldSEH := runtime.GetSEH()
|
||||
defer func() {
|
||||
s := recover()
|
||||
if s == nil {
|
||||
@ -207,9 +195,6 @@ func TestCallbackPanicLocked(t *testing.T) {
|
||||
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")
|
||||
|
Loading…
Reference in New Issue
Block a user