diff --git a/src/runtime/signal_windows_test.go b/src/runtime/signal_windows_test.go index 8b32ad89746c3e..fe74ad56bf99be 100644 --- a/src/runtime/signal_windows_test.go +++ b/src/runtime/signal_windows_test.go @@ -17,6 +17,61 @@ import ( "testing" ) +func TestVectoredHandlerExceptionInNonGoThread(t *testing.T) { + if *flagQuick { + t.Skip("-quick") + } + testenv.MustHaveGoBuild(t) + testenv.MustHaveCGO(t) + testenv.MustHaveExecPath(t, "g++") + testprog.Lock() + defer testprog.Unlock() + dir := t.TempDir() + + // build c program + dll := filepath.Join(dir, "veh.dll") + cmd := exec.Command("g++", "-shared", "-o", dll, "testdata/testwinlibthrow/veh.cpp", "-static", "-lstdc++") + out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() + if err != nil { + t.Fatalf("failed to build c exe: %s\n%s", err, out) + } + + // build go exe + exe := filepath.Join(dir, "test.exe") + cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinlibthrow/main.go") + out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() + if err != nil { + t.Fatalf("failed to build go library: %s\n%s", err, out) + } + + // run test program in same thread + cmd = exec.Command(exe) + out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() + if err == nil { + t.Fatal("error expected") + } + if _, ok := err.(*exec.ExitError); ok && len(out) > 0 { + if !bytes.Contains(out, []byte("Exception 0x2a")) { + t.Fatalf("unexpected failure while running executable: %s\n%s", err, out) + } + } else { + t.Fatalf("unexpected error while running executable: %s\n%s", err, out) + } + // run test program in a new thread + cmd = exec.Command(exe, "thread") + out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() + if err == nil { + t.Fatal("error expected") + } + if err, ok := err.(*exec.ExitError); ok { + if err.ExitCode() != 42 { + t.Fatalf("unexpected failure while running executable: %s\n%s", err, out) + } + } else { + t.Fatalf("unexpected error while running executable: %s\n%s", err, out) + } +} + func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) { if *flagQuick { t.Skip("-quick") diff --git a/src/runtime/sys_windows_arm.s b/src/runtime/sys_windows_arm.s index 5dc576a260986a..db6d8f1a08741d 100644 --- a/src/runtime/sys_windows_arm.s +++ b/src/runtime/sys_windows_arm.s @@ -123,8 +123,14 @@ TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0 MOVW R1, R7 // Save param1 BL runtime·load_g(SB) - CMP $0, g // is there a current g? - BL.EQ runtime·badsignal2(SB) + CMP $0, g // is there a current g? + BNE g_ok + ADD $(8+20), R13 // free locals + MOVM.IA.W (R13), [R3, R4-R11, R14] // pop {r3, r4-r11, lr} + MOVW $0, R0 // continue + BEQ return + +g_ok: // save g and SP in case of stack switch MOVW R13, 24(R13) diff --git a/src/runtime/sys_windows_arm64.s b/src/runtime/sys_windows_arm64.s index b39df4f1241be1..4702a4d7d254f7 100644 --- a/src/runtime/sys_windows_arm64.s +++ b/src/runtime/sys_windows_arm64.s @@ -147,10 +147,15 @@ TEXT sigtramp<>(SB),NOSPLIT|NOFRAME,$0 MOVD g, R17 // saved R28 (callee-save from Windows, not really g) BL runtime·load_g(SB) // smashes R0, R27, R28 (g) - CMP $0, g // is there a current g? - BNE 2(PC) - BL runtime·badsignal2(SB) + CMP $0, g // is there a current g? + BNE g_ok + MOVD R7, LR + MOVD R16, R27 // restore R27 + MOVD R17, g // restore R28 + MOVD $0, R0 // continue + RET +g_ok: // Do we need to switch to the g0 stack? MOVD g, R3 // R3 = oldg (for sigtramp_g0) MOVD g_m(g), R2 // R2 = m diff --git a/src/runtime/testdata/testwinlibthrow/main.go b/src/runtime/testdata/testwinlibthrow/main.go new file mode 100644 index 00000000000000..50c483f401edb3 --- /dev/null +++ b/src/runtime/testdata/testwinlibthrow/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "os" + "syscall" +) + +func main() { + dll := syscall.MustLoadDLL("veh.dll") + RaiseExcept := dll.MustFindProc("RaiseExcept") + RaiseNoExcept := dll.MustFindProc("RaiseNoExcept") + ThreadRaiseExcept := dll.MustFindProc("ThreadRaiseExcept") + ThreadRaiseNoExcept := dll.MustFindProc("ThreadRaiseNoExcept") + + thread := len(os.Args) > 1 && os.Args[1] == "thread" + if !thread { + RaiseExcept.Call() + RaiseNoExcept.Call() + } else { + ThreadRaiseExcept.Call() + ThreadRaiseNoExcept.Call() + } +} diff --git a/src/runtime/testdata/testwinlibthrow/veh.cpp b/src/runtime/testdata/testwinlibthrow/veh.cpp new file mode 100644 index 00000000000000..ed7015a0641809 --- /dev/null +++ b/src/runtime/testdata/testwinlibthrow/veh.cpp @@ -0,0 +1,53 @@ +//go:build ignore + +#include + +extern "C" __declspec(dllexport) +void RaiseExcept(void) +{ + try + { + RaiseException(42, 0, 0, 0); + } + catch (...) + { + } +} + +extern "C" __declspec(dllexport) +void RaiseNoExcept(void) +{ + RaiseException(42, 0, 0, 0); +} + +static DWORD WINAPI ThreadRaiser(void* Context) +{ + if (Context) + RaiseExcept(); + else + RaiseNoExcept(); + return 0; +} + +static void ThreadRaiseXxx(int except) +{ + static int dummy; + HANDLE thread = CreateThread(0, 0, ThreadRaiser, except ? &dummy : 0, 0, 0); + if (0 != thread) + { + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + } +} + +extern "C" __declspec(dllexport) +void ThreadRaiseExcept(void) +{ + ThreadRaiseXxx(1); +} + +extern "C" __declspec(dllexport) +void ThreadRaiseNoExcept(void) +{ + ThreadRaiseXxx(0); +}