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