Commit afe0e97a authored by Alex Brainman's avatar Alex Brainman

runtime: handle windows exceptions, even in cgo programs

Fixes #3543.

R=golang-dev, kardianos, rsc
CC=golang-dev, hectorchu, vcc.163
https://golang.org/cl/6245063
parent 034fa90d
...@@ -227,6 +227,8 @@ ok: ...@@ -227,6 +227,8 @@ ok:
aggr = "gobuf"; aggr = "gobuf";
else if(streq(fields.p[1], "WinCall")) else if(streq(fields.p[1], "WinCall"))
aggr = "wincall"; aggr = "wincall";
else if(streq(fields.p[1], "SEH"))
aggr = "seh";
} }
if(hasprefix(lines.p[i], "}")) if(hasprefix(lines.p[i], "}"))
aggr = nil; aggr = nil;
......
// 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})
}
// 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")
}
`
...@@ -28,5 +28,7 @@ uint32 runtime·ctrlhandler(uint32 type); ...@@ -28,5 +28,7 @@ uint32 runtime·ctrlhandler(uint32 type);
byte *runtime·compilecallback(Eface fn, bool cleanstack); byte *runtime·compilecallback(Eface fn, bool cleanstack);
void *runtime·callbackasm(void); void *runtime·callbackasm(void);
void runtime·install_exception_handler(void);
// TODO(brainman): should not need those // TODO(brainman): should not need those
#define NSIG 65 #define NSIG 65
...@@ -758,6 +758,11 @@ runtime·starttheworld(void) ...@@ -758,6 +758,11 @@ runtime·starttheworld(void)
void void
runtime·mstart(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) if(g != m->g0)
runtime·throw("bad runtime·mstart"); runtime·throw("bad runtime·mstart");
...@@ -766,6 +771,7 @@ runtime·mstart(void) ...@@ -766,6 +771,7 @@ runtime·mstart(void)
// so other calls can reuse this stack space. // so other calls can reuse this stack space.
runtime·gosave(&m->g0->sched); runtime·gosave(&m->g0->sched);
m->g0->sched.pc = (void*)-1; // make sure it is never used m->g0->sched.pc = (void*)-1; // make sure it is never used
m->seh = &seh;
runtime·asminit(); runtime·asminit();
runtime·minit(); runtime·minit();
...@@ -775,6 +781,10 @@ runtime·mstart(void) ...@@ -775,6 +781,10 @@ runtime·mstart(void)
runtime·initsig(); runtime·initsig();
schedule(nil); 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 // When running with cgo, we call libcgo_thread_start
......
...@@ -3,11 +3,6 @@ ...@@ -3,11 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
TEXT _rt0_386_windows(SB),7,$0 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) JMP _rt0_386(SB)
DATA runtime·iswindows(SB)/4, $1 DATA runtime·iswindows(SB)/4, $1
......
...@@ -69,6 +69,7 @@ typedef struct Hchan Hchan; ...@@ -69,6 +69,7 @@ typedef struct Hchan Hchan;
typedef struct Complex64 Complex64; typedef struct Complex64 Complex64;
typedef struct Complex128 Complex128; typedef struct Complex128 Complex128;
typedef struct WinCall WinCall; typedef struct WinCall WinCall;
typedef struct SEH SEH;
typedef struct Timers Timers; typedef struct Timers Timers;
typedef struct Timer Timer; typedef struct Timer Timer;
typedef struct GCStats GCStats; typedef struct GCStats GCStats;
...@@ -262,6 +263,7 @@ struct M ...@@ -262,6 +263,7 @@ struct M
#ifdef GOOS_windows #ifdef GOOS_windows
void* thread; // thread handle void* thread; // thread handle
#endif #endif
SEH* seh;
uintptr end[]; uintptr end[];
}; };
...@@ -316,6 +318,11 @@ struct WinCall ...@@ -316,6 +318,11 @@ struct WinCall
uintptr r2; uintptr r2;
uintptr err; // error number uintptr err; // error number
}; };
struct SEH
{
void* prev;
void* handler;
};
#ifdef GOOS_windows #ifdef GOOS_windows
enum { enum {
......
...@@ -243,11 +243,6 @@ TEXT runtime·tstart(SB),7,$0 ...@@ -243,11 +243,6 @@ TEXT runtime·tstart(SB),7,$0
MOVL newm+4(SP), CX // m MOVL newm+4(SP), CX // m
MOVL m_g0(CX), DX // g 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. // Layout new m scheduler stack on os stack.
MOVL SP, AX MOVL SP, AX
MOVL AX, g_stackbase(DX) MOVL AX, g_stackbase(DX)
...@@ -267,11 +262,6 @@ TEXT runtime·tstart(SB),7,$0 ...@@ -267,11 +262,6 @@ TEXT runtime·tstart(SB),7,$0
CALL runtime·mstart(SB) CALL runtime·mstart(SB)
// Pop SEH frame
MOVL 0(FS), SP
POPL 0(FS)
POPL CX
RET RET
// uint32 tstart_stdcall(M *newm); // uint32 tstart_stdcall(M *newm);
...@@ -296,3 +286,20 @@ TEXT runtime·setldt(SB),7,$0 ...@@ -296,3 +286,20 @@ TEXT runtime·setldt(SB),7,$0
MOVL address+4(FP), CX MOVL address+4(FP), CX
MOVL CX, 0x14(FS) MOVL CX, 0x14(FS)
RET 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
...@@ -328,7 +328,6 @@ TEXT runtime·tstart_stdcall(SB),7,$0 ...@@ -328,7 +328,6 @@ TEXT runtime·tstart_stdcall(SB),7,$0
// Someday the convention will be D is always cleared. // Someday the convention will be D is always cleared.
CLD CLD
CALL runtime·setstacklimits(SB)
CALL runtime·stackcheck(SB) // clobbers AX,CX CALL runtime·stackcheck(SB) // clobbers AX,CX
CALL runtime·mstart(SB) CALL runtime·mstart(SB)
...@@ -337,6 +336,10 @@ TEXT runtime·tstart_stdcall(SB),7,$0 ...@@ -337,6 +336,10 @@ TEXT runtime·tstart_stdcall(SB),7,$0
// set tls base to DI // set tls base to DI
TEXT runtime·settls(SB),7,$0 TEXT runtime·settls(SB),7,$0
CALL runtime·setstacklimits(SB)
MOVQ DI, 0x28(GS) MOVQ DI, 0x28(GS)
RET RET
// void install_exception_handler()
TEXT runtime·install_exception_handler(SB),7,$0
CALL runtime·setstacklimits(SB)
RET
...@@ -208,6 +208,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) ...@@ -208,6 +208,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
void void
runtime·minit(void) runtime·minit(void)
{ {
runtime·install_exception_handler();
} }
int64 int64
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment