Skip to content

Commit afe0e97

Browse files
committed
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
1 parent 034fa90 commit afe0e97

File tree

10 files changed

+159
-17
lines changed

10 files changed

+159
-17
lines changed

src/cmd/dist/buildruntime.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ mkzasm(char *dir, char *file)
227227
aggr = "gobuf";
228228
else if(streq(fields.p[1], "WinCall"))
229229
aggr = "wincall";
230+
else if(streq(fields.p[1], "SEH"))
231+
aggr = "seh";
230232
}
231233
if(hasprefix(lines.p[i], "}"))
232234
aggr = nil;

src/pkg/runtime/crash_cgo_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2012 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build cgo
6+
7+
package runtime_test
8+
9+
import (
10+
"testing"
11+
)
12+
13+
func TestCgoCrashHandler(t *testing.T) {
14+
testCrashHandler(t, &crashTest{Cgo: true})
15+
}

src/pkg/runtime/crash_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright 2012 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package runtime_test
6+
7+
import (
8+
"io/ioutil"
9+
"os"
10+
"os/exec"
11+
"path/filepath"
12+
"testing"
13+
"text/template"
14+
)
15+
16+
type crashTest struct {
17+
Cgo bool
18+
}
19+
20+
// This test is a separate program, because it is testing
21+
// both main (m0) and non-main threads (m).
22+
23+
func testCrashHandler(t *testing.T, ct *crashTest) {
24+
st := template.Must(template.New("crashSource").Parse(crashSource))
25+
26+
dir, err := ioutil.TempDir("", "go-build")
27+
if err != nil {
28+
t.Fatalf("failed to create temp directory: %v", err)
29+
}
30+
defer os.RemoveAll(dir)
31+
32+
src := filepath.Join(dir, "main.go")
33+
f, err := os.Create(src)
34+
if err != nil {
35+
t.Fatalf("failed to create %v: %v", src, err)
36+
}
37+
err = st.Execute(f, ct)
38+
if err != nil {
39+
f.Close()
40+
t.Fatalf("failed to execute template: %v", err)
41+
}
42+
f.Close()
43+
44+
got, err := exec.Command("go", "run", src).CombinedOutput()
45+
if err != nil {
46+
t.Fatalf("program exited with error: %v\n%v", err, string(got))
47+
}
48+
want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
49+
if string(got) != string(want) {
50+
t.Fatalf("expected %q, but got %q", string(want), string(got))
51+
}
52+
}
53+
54+
func TestCrashHandler(t *testing.T) {
55+
testCrashHandler(t, &crashTest{Cgo: false})
56+
}
57+
58+
const crashSource = `
59+
package main
60+
61+
import (
62+
"fmt"
63+
"runtime"
64+
)
65+
66+
{{if .Cgo}}
67+
import "C"
68+
{{end}}
69+
70+
func test(name string) {
71+
defer func() {
72+
if x := recover(); x != nil {
73+
fmt.Printf(" recovered")
74+
}
75+
fmt.Printf(" done\n")
76+
}()
77+
fmt.Printf("%s:", name)
78+
var s *string
79+
_ = *s
80+
fmt.Print("SHOULD NOT BE HERE")
81+
}
82+
83+
func testInNewThread(name string) {
84+
c := make(chan bool)
85+
go func() {
86+
runtime.LockOSThread()
87+
test(name)
88+
c <- true
89+
}()
90+
<-c
91+
}
92+
93+
func main() {
94+
runtime.LockOSThread()
95+
test("main")
96+
testInNewThread("new-thread")
97+
testInNewThread("second-new-thread")
98+
test("main-again")
99+
}
100+
`

src/pkg/runtime/os_windows.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,7 @@ uint32 runtime·ctrlhandler(uint32 type);
2828
byte *runtime·compilecallback(Eface fn, bool cleanstack);
2929
void *runtime·callbackasm(void);
3030

31+
void runtime·install_exception_handler(void);
32+
3133
// TODO(brainman): should not need those
3234
#define NSIG 65

src/pkg/runtime/proc.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,11 @@ runtime·starttheworld(void)
758758
void
759759
runtime·mstart(void)
760760
{
761+
// It is used by windows-386 only. Unfortunately, seh needs
762+
// to be located on os stack, and mstart runs on os stack
763+
// for both m0 and m.
764+
SEH seh;
765+
761766
if(g != m->g0)
762767
runtime·throw("bad runtime·mstart");
763768

@@ -766,6 +771,7 @@ runtime·mstart(void)
766771
// so other calls can reuse this stack space.
767772
runtime·gosave(&m->g0->sched);
768773
m->g0->sched.pc = (void*)-1; // make sure it is never used
774+
m->seh = &seh;
769775
runtime·asminit();
770776
runtime·minit();
771777

@@ -775,6 +781,10 @@ runtime·mstart(void)
775781
runtime·initsig();
776782

777783
schedule(nil);
784+
785+
// TODO(brainman): This point is never reached, because scheduler
786+
// does not release os threads at the moment. But once this path
787+
// is enabled, we must remove our seh here.
778788
}
779789

780790
// When running with cgo, we call libcgo_thread_start

src/pkg/runtime/rt0_windows_386.s

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@
33
// license that can be found in the LICENSE file.
44

55
TEXT _rt0_386_windows(SB),7,$0
6-
// Set up SEH frame for bootstrap m
7-
PUSHL $runtime·sigtramp(SB)
8-
PUSHL 0(FS)
9-
MOVL SP, 0(FS)
10-
116
JMP _rt0_386(SB)
127

138
DATA runtime·iswindows(SB)/4, $1

src/pkg/runtime/runtime.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ typedef struct Hchan Hchan;
6969
typedef struct Complex64 Complex64;
7070
typedef struct Complex128 Complex128;
7171
typedef struct WinCall WinCall;
72+
typedef struct SEH SEH;
7273
typedef struct Timers Timers;
7374
typedef struct Timer Timer;
7475
typedef struct GCStats GCStats;
@@ -262,6 +263,7 @@ struct M
262263
#ifdef GOOS_windows
263264
void* thread; // thread handle
264265
#endif
266+
SEH* seh;
265267
uintptr end[];
266268
};
267269

@@ -316,6 +318,11 @@ struct WinCall
316318
uintptr r2;
317319
uintptr err; // error number
318320
};
321+
struct SEH
322+
{
323+
void* prev;
324+
void* handler;
325+
};
319326

320327
#ifdef GOOS_windows
321328
enum {

src/pkg/runtime/sys_windows_386.s

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,6 @@ TEXT runtime·tstart(SB),7,$0
243243
MOVL newm+4(SP), CX // m
244244
MOVL m_g0(CX), DX // g
245245

246-
// Set up SEH frame
247-
PUSHL $runtime·sigtramp(SB)
248-
PUSHL 0(FS)
249-
MOVL SP, 0(FS)
250-
251246
// Layout new m scheduler stack on os stack.
252247
MOVL SP, AX
253248
MOVL AX, g_stackbase(DX)
@@ -267,11 +262,6 @@ TEXT runtime·tstart(SB),7,$0
267262

268263
CALL runtime·mstart(SB)
269264

270-
// Pop SEH frame
271-
MOVL 0(FS), SP
272-
POPL 0(FS)
273-
POPL CX
274-
275265
RET
276266

277267
// uint32 tstart_stdcall(M *newm);
@@ -296,3 +286,20 @@ TEXT runtime·setldt(SB),7,$0
296286
MOVL address+4(FP), CX
297287
MOVL CX, 0x14(FS)
298288
RET
289+
290+
// void install_exception_handler()
291+
TEXT runtime·install_exception_handler(SB),7,$0
292+
get_tls(CX)
293+
MOVL m(CX), CX // m
294+
295+
// Set up SEH frame
296+
MOVL m_seh(CX), DX
297+
MOVL $runtime·sigtramp(SB), AX
298+
MOVL AX, seh_handler(DX)
299+
MOVL 0(FS), AX
300+
MOVL AX, seh_prev(DX)
301+
302+
// Install it
303+
MOVL DX, 0(FS)
304+
305+
RET

src/pkg/runtime/sys_windows_amd64.s

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,6 @@ TEXT runtime·tstart_stdcall(SB),7,$0
328328
// Someday the convention will be D is always cleared.
329329
CLD
330330

331-
CALL runtime·setstacklimits(SB)
332331
CALL runtime·stackcheck(SB) // clobbers AX,CX
333332
CALL runtime·mstart(SB)
334333

@@ -337,6 +336,10 @@ TEXT runtime·tstart_stdcall(SB),7,$0
337336

338337
// set tls base to DI
339338
TEXT runtime·settls(SB),7,$0
340-
CALL runtime·setstacklimits(SB)
341339
MOVQ DI, 0x28(GS)
342340
RET
341+
342+
// void install_exception_handler()
343+
TEXT runtime·install_exception_handler(SB),7,$0
344+
CALL runtime·setstacklimits(SB)
345+
RET

src/pkg/runtime/thread_windows.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
208208
void
209209
runtime·minit(void)
210210
{
211+
runtime·install_exception_handler();
211212
}
212213

213214
int64

0 commit comments

Comments
 (0)