]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: handle windows exceptions, even in cgo programs
authorAlex Brainman <alex.brainman@gmail.com>
Wed, 30 May 2012 05:10:54 +0000 (15:10 +1000)
committerAlex Brainman <alex.brainman@gmail.com>
Wed, 30 May 2012 05:10:54 +0000 (15:10 +1000)
Fixes #3543.

R=golang-dev, kardianos, rsc
CC=golang-dev, hectorchu, vcc.163
https://golang.org/cl/6245063

src/cmd/dist/buildruntime.c
src/pkg/runtime/crash_cgo_test.go [new file with mode: 0644]
src/pkg/runtime/crash_test.go [new file with mode: 0644]
src/pkg/runtime/os_windows.h
src/pkg/runtime/proc.c
src/pkg/runtime/rt0_windows_386.s
src/pkg/runtime/runtime.h
src/pkg/runtime/sys_windows_386.s
src/pkg/runtime/sys_windows_amd64.s
src/pkg/runtime/thread_windows.c

index 454d594e5d51cf80145dc8bb41cb37606561c780..d3ab197461f015a69a17c6dae6ad6bd746933645 100644 (file)
@@ -227,6 +227,8 @@ ok:
                                aggr = "gobuf";
                        else if(streq(fields.p[1], "WinCall"))
                                aggr = "wincall";
+                       else if(streq(fields.p[1], "SEH"))
+                               aggr = "seh";
                }
                if(hasprefix(lines.p[i], "}"))
                        aggr = nil;
diff --git a/src/pkg/runtime/crash_cgo_test.go b/src/pkg/runtime/crash_cgo_test.go
new file mode 100644 (file)
index 0000000..91c4bdb
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build cgo
+
+package runtime_test
+
+import (
+       "testing"
+)
+
+func TestCgoCrashHandler(t *testing.T) {
+       testCrashHandler(t, &crashTest{Cgo: true})
+}
diff --git a/src/pkg/runtime/crash_test.go b/src/pkg/runtime/crash_test.go
new file mode 100644 (file)
index 0000000..bc6b89f
--- /dev/null
@@ -0,0 +1,100 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime_test
+
+import (
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "testing"
+       "text/template"
+)
+
+type crashTest struct {
+       Cgo bool
+}
+
+// This test is a separate program, because it is testing
+// both main (m0) and non-main threads (m).
+
+func testCrashHandler(t *testing.T, ct *crashTest) {
+       st := template.Must(template.New("crashSource").Parse(crashSource))
+
+       dir, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatalf("failed to create temp directory: %v", err)
+       }
+       defer os.RemoveAll(dir)
+
+       src := filepath.Join(dir, "main.go")
+       f, err := os.Create(src)
+       if err != nil {
+               t.Fatalf("failed to create %v: %v", src, err)
+       }
+       err = st.Execute(f, ct)
+       if err != nil {
+               f.Close()
+               t.Fatalf("failed to execute template: %v", err)
+       }
+       f.Close()
+
+       got, err := exec.Command("go", "run", src).CombinedOutput()
+       if err != nil {
+               t.Fatalf("program exited with error: %v\n%v", err, string(got))
+       }
+       want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
+       if string(got) != string(want) {
+               t.Fatalf("expected %q, but got %q", string(want), string(got))
+       }
+}
+
+func TestCrashHandler(t *testing.T) {
+       testCrashHandler(t, &crashTest{Cgo: false})
+}
+
+const crashSource = `
+package main
+
+import (
+       "fmt"
+       "runtime"
+)
+
+{{if .Cgo}}
+import "C"
+{{end}}
+
+func test(name string) {
+       defer func() {
+               if x := recover(); x != nil {
+                       fmt.Printf(" recovered")
+               }
+               fmt.Printf(" done\n")
+       }()
+       fmt.Printf("%s:", name)
+       var s *string
+       _ = *s
+       fmt.Print("SHOULD NOT BE HERE")
+}
+
+func testInNewThread(name string) {
+       c := make(chan bool)
+       go func() {
+               runtime.LockOSThread()
+               test(name)
+               c <- true
+       }()
+       <-c
+}
+
+func main() {
+       runtime.LockOSThread()
+       test("main")
+       testInNewThread("new-thread")
+       testInNewThread("second-new-thread")
+       test("main-again")
+}
+`
index 9d387b7ad6a2d4a8208077e032575b98d90f5707..e8962265d5423ac1a91e0e7845608d1aaa15275c 100644 (file)
@@ -28,5 +28,7 @@ uint32 runtime·ctrlhandler(uint32 type);
 byte *runtime·compilecallback(Eface fn, bool cleanstack);
 void *runtime·callbackasm(void);
 
+void runtime·install_exception_handler(void);
+
 // TODO(brainman): should not need those
 #define        NSIG    65
index 81decd6bf85387ea4186919da808c1fbc23b5be3..fbc262904b1d5f36c05cd4fcdf0998fc12a95202 100644 (file)
@@ -758,6 +758,11 @@ runtime·starttheworld(void)
 void
 runtime·mstart(void)
 {
+       // 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;
+
        if(g != m->g0)
                runtime·throw("bad runtime·mstart");
 
@@ -766,6 +771,7 @@ runtime·mstart(void)
        // so other calls can reuse this stack space.
        runtime·gosave(&m->g0->sched);
        m->g0->sched.pc = (void*)-1;  // make sure it is never used
+       m->seh = &seh;
        runtime·asminit();
        runtime·minit();
 
@@ -775,6 +781,10 @@ runtime·mstart(void)
                runtime·initsig();
 
        schedule(nil);
+
+       // TODO(brainman): This point is never reached, because scheduler
+       // does not release os threads at the moment. But once this path
+       // is enabled, we must remove our seh here.
 }
 
 // When running with cgo, we call libcgo_thread_start
index 3b023de2f6a536d8e3f9b894d02e9ffeaa18281e..a06aa787e2e1c312bb95b809427c017eb20a50fc 100644 (file)
@@ -3,11 +3,6 @@
 // license that can be found in the LICENSE file.
 
 TEXT _rt0_386_windows(SB),7,$0
-       // Set up SEH frame for bootstrap m
-       PUSHL   $runtime·sigtramp(SB)
-       PUSHL   0(FS)
-       MOVL    SP, 0(FS)
-
        JMP     _rt0_386(SB)
 
 DATA  runtime·iswindows(SB)/4, $1
index 665d477f7d93ae9850d84918745824bf69eb11ad..8a7c9c68a51756ea513590ca461571658935044e 100644 (file)
@@ -69,6 +69,7 @@ typedef       struct  Hchan           Hchan;
 typedef        struct  Complex64       Complex64;
 typedef        struct  Complex128      Complex128;
 typedef        struct  WinCall         WinCall;
+typedef        struct  SEH             SEH;
 typedef        struct  Timers          Timers;
 typedef        struct  Timer           Timer;
 typedef struct GCStats         GCStats;
@@ -262,6 +263,7 @@ struct      M
 #ifdef GOOS_windows
        void*   thread;         // thread handle
 #endif
+       SEH*    seh;
        uintptr end[];
 };
 
@@ -316,6 +318,11 @@ struct     WinCall
        uintptr r2;
        uintptr err;    // error number
 };
+struct SEH
+{
+       void*   prev;
+       void*   handler;
+};
 
 #ifdef GOOS_windows
 enum {
index d5646bfea1a67a8172129b25db097d88bc46d37c..ab6d7f2209044e0de84bef2b7cec2e44cac493ba 100644 (file)
@@ -243,11 +243,6 @@ TEXT runtime·tstart(SB),7,$0
        MOVL    newm+4(SP), CX          // m
        MOVL    m_g0(CX), DX            // g
 
-       // Set up SEH frame
-       PUSHL   $runtime·sigtramp(SB)
-       PUSHL   0(FS)
-       MOVL    SP, 0(FS)
-
        // Layout new m scheduler stack on os stack.
        MOVL    SP, AX
        MOVL    AX, g_stackbase(DX)
@@ -267,11 +262,6 @@ TEXT runtime·tstart(SB),7,$0
 
        CALL    runtime·mstart(SB)
 
-       // Pop SEH frame
-       MOVL    0(FS), SP
-       POPL    0(FS)
-       POPL    CX
-
        RET
 
 // uint32 tstart_stdcall(M *newm);
@@ -296,3 +286,20 @@ TEXT runtime·setldt(SB),7,$0
        MOVL    address+4(FP), CX
        MOVL    CX, 0x14(FS)
        RET
+
+// void install_exception_handler()
+TEXT runtime·install_exception_handler(SB),7,$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
index 11909cda278aa19b9d999ae07cec68aa762c7516..b2b8de5025a985a10746bcc937330dc99bc8904f 100644 (file)
@@ -328,7 +328,6 @@ TEXT runtime·tstart_stdcall(SB),7,$0
        // Someday the convention will be D is always cleared.
        CLD
 
-       CALL    runtime·setstacklimits(SB)
        CALL    runtime·stackcheck(SB) // clobbers AX,CX
        CALL    runtime·mstart(SB)
 
@@ -337,6 +336,10 @@ TEXT runtime·tstart_stdcall(SB),7,$0
 
 // set tls base to DI
 TEXT runtime·settls(SB),7,$0
-       CALL    runtime·setstacklimits(SB)
        MOVQ    DI, 0x28(GS)
        RET
+
+// void install_exception_handler()
+TEXT runtime·install_exception_handler(SB),7,$0
+       CALL    runtime·setstacklimits(SB)
+       RET
index f684d373351ab25f4a943422614c8b8a781576d4..5f893c1613aaf1b5d5086477fb97a7865aacd541 100644 (file)
@@ -208,6 +208,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
 void
 runtime·minit(void)
 {
+       runtime·install_exception_handler();
 }
 
 int64