]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: use VEH, not SEH, for windows/386 exception handling
authorRuss Cox <rsc@golang.org>
Tue, 25 Mar 2014 01:22:16 +0000 (21:22 -0400)
committerRuss Cox <rsc@golang.org>
Tue, 25 Mar 2014 01:22:16 +0000 (21:22 -0400)
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

src/pkg/runtime/asm_386.s
src/pkg/runtime/cgocall.c
src/pkg/runtime/export_test.go
src/pkg/runtime/os_windows.c
src/pkg/runtime/os_windows_386.c
src/pkg/runtime/proc.c
src/pkg/runtime/runtime.h
src/pkg/runtime/runtime1.goc
src/pkg/runtime/sys_windows_386.s
src/pkg/runtime/syscall_windows_test.go

index df2ed464e52b54eaf916b909484875c88a255137..bb3bcaf34844f256a672c2c9ce4bad30a7f90116 100644 (file)
@@ -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)
index 7b0f7a8f36ab2bac5d340b80a63833b824b6dc1c..9ae4fa057bfc88afb163340454bb50a846e75fd5 100644 (file)
@@ -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);
 }
index eedc1b7e20ea46a4b5c084882207fb3acd5202e2..7a31b63b31fca74f3ecee1e28edc0097e20ab38c 100644 (file)
@@ -90,7 +90,3 @@ var MemclrBytes = memclrBytes
 func gogoBytes() int32
 
 var GogoBytes = gogoBytes
-
-func getseh_go() uintptr
-
-var GetSEH = getseh_go
index a84b4e2830108fbdbdf8397bc0e412e09ce55662..af03247418f90ebaebd131dc48359d6020b18128 100644 (file)
@@ -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"
 #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)
 {
index f6b54316948e601c3358de3f7a86a62b64ee75da..b49f7b61709598799e8e561d1dafe08f238b72b6 100644 (file)
@@ -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
index b5093497d9af6048d1638a54256f925fc3eeff42..375dced240465a0511a5449a27cd8f29034ea6c9 100644 (file)
@@ -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;
index baa751cd72ba819d9af991972dc7dad4d50db5a7..9cb6960c624def66b89bdaca36b02c81c1bbdf24 100644 (file)
@@ -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
index 57a476b7ce5332a0b1b136dd640aa71aeb5bee9c..c6f6b626a7164645a1c89b47eed433f3f6e6b9eb 100644 (file)
@@ -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;
 }
index ba872496d619e860fe45a8b5401a2ac1da2de38b..26dc30094faed9a32e9f545dc085bd6ed0e4e59e 100644 (file)
@@ -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
-
-       // 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)
@@ -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
-
index d5e35b9bc3a489301a097c50433d6d89ed58fdf2..fabf935d8e246da95f24993bd1a215ec995dc47b 100644 (file)
@@ -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")