Source file
src/runtime/runtime-seh_windows_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "internal/abi"
9 "internal/syscall/windows"
10 "runtime"
11 "slices"
12 "testing"
13 "unsafe"
14 )
15
16 func sehf1() int {
17 return sehf1()
18 }
19
20 func sehf2() {}
21
22 func TestSehLookupFunctionEntry(t *testing.T) {
23 if runtime.GOARCH != "amd64" {
24 t.Skip("skipping amd64-only test")
25 }
26
27
28
29
30
31
32
33 sehf1pc := abi.FuncPCABIInternal(sehf1)
34 var fnwithframe func()
35 fnwithframe = func() {
36 fnwithframe()
37 }
38 fnwithoutframe := func() {}
39 tests := []struct {
40 name string
41 pc uintptr
42 hasframe bool
43 }{
44 {"no frame func", abi.FuncPCABIInternal(sehf2), false},
45 {"no func", sehf1pc - 1, false},
46 {"func at entry", sehf1pc, true},
47 {"func in prologue", sehf1pc + 1, true},
48 {"anonymous func with frame", abi.FuncPCABIInternal(fnwithframe), true},
49 {"anonymous func without frame", abi.FuncPCABIInternal(fnwithoutframe), false},
50 {"pc at func body", runtime.NewContextStub().GetPC(), true},
51 }
52 for _, tt := range tests {
53 var base uintptr
54 fn := windows.RtlLookupFunctionEntry(tt.pc, &base, nil)
55 if !tt.hasframe {
56 if fn != 0 {
57 t.Errorf("%s: unexpected frame", tt.name)
58 }
59 continue
60 }
61 if fn == 0 {
62 t.Errorf("%s: missing frame", tt.name)
63 }
64 }
65 }
66
67 func sehCallers() []uintptr {
68
69
70
71 ctx := runtime.NewContextStub()
72
73 pcs := make([]uintptr, 15)
74 var base, frame uintptr
75 var n int
76 for i := 0; i < len(pcs); i++ {
77 fn := windows.RtlLookupFunctionEntry(ctx.GetPC(), &base, nil)
78 if fn == 0 {
79 break
80 }
81 pcs[i] = ctx.GetPC()
82 n++
83 windows.RtlVirtualUnwind(0, base, ctx.GetPC(), fn, uintptr(unsafe.Pointer(ctx)), nil, &frame, nil)
84 }
85 return pcs[:n]
86 }
87
88
89
90
91 func sehf3(pan bool) []uintptr {
92 return sehf4(pan)
93 }
94
95
96 func sehf4(pan bool) []uintptr {
97 var pcs []uintptr
98 if pan {
99 panic("sehf4")
100 }
101 pcs = sehCallers()
102 return pcs
103 }
104
105 func testSehCallersEqual(t *testing.T, pcs []uintptr, want []string) {
106 t.Helper()
107 got := make([]string, 0, len(want))
108 for _, pc := range pcs {
109 fn := runtime.FuncForPC(pc)
110 if fn == nil || len(got) >= len(want) {
111 break
112 }
113 name := fn.Name()
114 switch name {
115 case "runtime.panicmem":
116
117
118 continue
119 }
120 got = append(got, name)
121 }
122 if !slices.Equal(want, got) {
123 t.Fatalf("wanted %v, got %v", want, got)
124 }
125 }
126
127 func TestSehUnwind(t *testing.T) {
128 if runtime.GOARCH != "amd64" {
129 t.Skip("skipping amd64-only test")
130 }
131 pcs := sehf3(false)
132 testSehCallersEqual(t, pcs, []string{"runtime_test.sehCallers", "runtime_test.sehf4",
133 "runtime_test.sehf3", "runtime_test.TestSehUnwind"})
134 }
135
136 func TestSehUnwindPanic(t *testing.T) {
137 if runtime.GOARCH != "amd64" {
138 t.Skip("skipping amd64-only test")
139 }
140 want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindPanic.func1", "runtime.gopanic",
141 "runtime_test.sehf4", "runtime_test.sehf3", "runtime_test.TestSehUnwindPanic"}
142 defer func() {
143 if r := recover(); r == nil {
144 t.Fatal("did not panic")
145 }
146 pcs := sehCallers()
147 testSehCallersEqual(t, pcs, want)
148 }()
149 sehf3(true)
150 }
151
152 func TestSehUnwindDoublePanic(t *testing.T) {
153 if runtime.GOARCH != "amd64" {
154 t.Skip("skipping amd64-only test")
155 }
156 want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindDoublePanic.func1.1", "runtime.gopanic",
157 "runtime_test.TestSehUnwindDoublePanic.func1", "runtime.gopanic", "runtime_test.TestSehUnwindDoublePanic"}
158 defer func() {
159 defer func() {
160 if recover() == nil {
161 t.Fatal("did not panic")
162 }
163 pcs := sehCallers()
164 testSehCallersEqual(t, pcs, want)
165 }()
166 if recover() == nil {
167 t.Fatal("did not panic")
168 }
169 panic(2)
170 }()
171 panic(1)
172 }
173
174 func TestSehUnwindNilPointerPanic(t *testing.T) {
175 if runtime.GOARCH != "amd64" {
176 t.Skip("skipping amd64-only test")
177 }
178 want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindNilPointerPanic.func1", "runtime.gopanic",
179 "runtime.sigpanic", "runtime_test.TestSehUnwindNilPointerPanic"}
180 defer func() {
181 if r := recover(); r == nil {
182 t.Fatal("did not panic")
183 }
184 pcs := sehCallers()
185 testSehCallersEqual(t, pcs, want)
186 }()
187 var p *int
188 if *p == 3 {
189 t.Fatal("did not see nil pointer panic")
190 }
191 }
192
View as plain text