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
This commit is contained in:
Alex Brainman 2012-05-30 15:10:54 +10:00
parent 034fa90dc1
commit afe0e97aa6
10 changed files with 159 additions and 17 deletions

View 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;

View File

@ -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})
}

View File

@ -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")
}
`

View 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

View 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

View 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

View 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 {

View 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

View 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

View 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