Source file
src/runtime/syscall_windows_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "fmt"
9 "internal/abi"
10 "internal/race"
11 "internal/runtime/syscall/windows"
12 "internal/syscall/windows/sysdll"
13 "internal/testenv"
14 "io"
15 "math"
16 "os"
17 "os/exec"
18 "path/filepath"
19 "reflect"
20 "runtime"
21 "strconv"
22 "strings"
23 "syscall"
24 "testing"
25 "unsafe"
26 )
27
28 type DLL struct {
29 *syscall.DLL
30 t *testing.T
31 }
32
33 func GetDLL(t *testing.T, name string) *DLL {
34 d, e := syscall.LoadDLL(name)
35 if e != nil {
36 t.Fatal(e)
37 }
38 return &DLL{DLL: d, t: t}
39 }
40
41 func (d *DLL) Proc(name string) *syscall.Proc {
42 p, e := d.FindProc(name)
43 if e != nil {
44 d.t.Fatal(e)
45 }
46 return p
47 }
48
49 func TestStdCall(t *testing.T) {
50 type Rect struct {
51 left, top, right, bottom int32
52 }
53 res := Rect{}
54 expected := Rect{1, 1, 40, 60}
55 a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call(
56 uintptr(unsafe.Pointer(&res)),
57 uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})),
58 uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50})))
59 if a != 1 || res.left != expected.left ||
60 res.top != expected.top ||
61 res.right != expected.right ||
62 res.bottom != expected.bottom {
63 t.Error("stdcall USER32.UnionRect returns", a, "res=", res)
64 }
65 }
66
67 func Test64BitReturnStdCall(t *testing.T) {
68
69 const (
70 VER_BUILDNUMBER = 0x0000004
71 VER_MAJORVERSION = 0x0000002
72 VER_MINORVERSION = 0x0000001
73 VER_PLATFORMID = 0x0000008
74 VER_PRODUCT_TYPE = 0x0000080
75 VER_SERVICEPACKMAJOR = 0x0000020
76 VER_SERVICEPACKMINOR = 0x0000010
77 VER_SUITENAME = 0x0000040
78
79 VER_EQUAL = 1
80 VER_GREATER = 2
81 VER_GREATER_EQUAL = 3
82 VER_LESS = 4
83 VER_LESS_EQUAL = 5
84
85 ERROR_OLD_WIN_VERSION syscall.Errno = 1150
86 )
87
88 type OSVersionInfoEx struct {
89 OSVersionInfoSize uint32
90 MajorVersion uint32
91 MinorVersion uint32
92 BuildNumber uint32
93 PlatformId uint32
94 CSDVersion [128]uint16
95 ServicePackMajor uint16
96 ServicePackMinor uint16
97 SuiteMask uint16
98 ProductType byte
99 Reserve byte
100 }
101
102 d := GetDLL(t, "kernel32.dll")
103
104 var m1, m2 uintptr
105 VerSetConditionMask := d.Proc("VerSetConditionMask")
106 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL)
107 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL)
108 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)
109 m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL)
110
111 vi := OSVersionInfoEx{
112 MajorVersion: 5,
113 MinorVersion: 1,
114 ServicePackMajor: 2,
115 ServicePackMinor: 0,
116 }
117 vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi))
118 r, _, e2 := d.Proc("VerifyVersionInfoW").Call(
119 uintptr(unsafe.Pointer(&vi)),
120 VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR,
121 m1, m2)
122 if r == 0 && e2 != ERROR_OLD_WIN_VERSION {
123 t.Errorf("VerifyVersionInfo failed: %s", e2)
124 }
125 }
126
127 func TestCDecl(t *testing.T) {
128 var buf [50]byte
129 fmtp, _ := syscall.BytePtrFromString("%d %d %d")
130 a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call(
131 uintptr(unsafe.Pointer(&buf[0])),
132 uintptr(unsafe.Pointer(fmtp)),
133 1000, 2000, 3000)
134 if string(buf[:a]) != "1000 2000 3000" {
135 t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
136 }
137 }
138
139 func TestEnumWindows(t *testing.T) {
140 d := GetDLL(t, "user32.dll")
141 isWindows := d.Proc("IsWindow")
142 counter := 0
143 cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
144 if lparam != 888 {
145 t.Error("lparam was not passed to callback")
146 }
147 b, _, _ := isWindows.Call(uintptr(hwnd))
148 if b == 0 {
149 t.Error("USER32.IsWindow returns FALSE")
150 }
151 counter++
152 return 1
153 })
154 a, _, _ := d.Proc("EnumWindows").Call(cb, 888)
155 if a == 0 {
156 t.Error("USER32.EnumWindows returns FALSE")
157 }
158 if counter == 0 {
159 t.Error("Callback has been never called or your have no windows")
160 }
161 }
162
163 func callback(timeFormatString unsafe.Pointer, lparam uintptr) uintptr {
164 (*(*func())(unsafe.Pointer(&lparam)))()
165 return 0
166 }
167
168
169 func nestedCall(t *testing.T, f func()) {
170 c := syscall.NewCallback(callback)
171 d := GetDLL(t, "kernel32.dll")
172 defer d.Release()
173 const LOCALE_NAME_USER_DEFAULT = 0
174 d.Proc("EnumTimeFormatsEx").Call(c, LOCALE_NAME_USER_DEFAULT, 0, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f))))
175 }
176
177 func TestCallback(t *testing.T) {
178 var x = false
179 nestedCall(t, func() { x = true })
180 if !x {
181 t.Fatal("nestedCall did not call func")
182 }
183 }
184
185 func TestCallbackGC(t *testing.T) {
186 nestedCall(t, runtime.GC)
187 }
188
189 func TestCallbackPanicLocked(t *testing.T) {
190 runtime.LockOSThread()
191 defer runtime.UnlockOSThread()
192
193 if !runtime.LockedOSThread() {
194 t.Fatal("runtime.LockOSThread didn't")
195 }
196 defer func() {
197 s := recover()
198 if s == nil {
199 t.Fatal("did not panic")
200 }
201 if s.(string) != "callback panic" {
202 t.Fatal("wrong panic:", s)
203 }
204 if !runtime.LockedOSThread() {
205 t.Fatal("lost lock on OS thread after panic")
206 }
207 }()
208 nestedCall(t, func() { panic("callback panic") })
209 panic("nestedCall returned")
210 }
211
212 func TestCallbackPanic(t *testing.T) {
213
214 if runtime.LockedOSThread() {
215 t.Fatal("locked OS thread on entry to TestCallbackPanic")
216 }
217 defer func() {
218 s := recover()
219 if s == nil {
220 t.Fatal("did not panic")
221 }
222 if s.(string) != "callback panic" {
223 t.Fatal("wrong panic:", s)
224 }
225 if runtime.LockedOSThread() {
226 t.Fatal("locked OS thread on exit from TestCallbackPanic")
227 }
228 }()
229 nestedCall(t, func() { panic("callback panic") })
230 panic("nestedCall returned")
231 }
232
233 func TestCallbackPanicLoop(t *testing.T) {
234
235 for i := 0; i < 100000; i++ {
236 TestCallbackPanic(t)
237 }
238 }
239
240 func TestBlockingCallback(t *testing.T) {
241 c := make(chan int)
242 go func() {
243 for i := 0; i < 10; i++ {
244 c <- <-c
245 }
246 }()
247 nestedCall(t, func() {
248 for i := 0; i < 10; i++ {
249 c <- i
250 if j := <-c; j != i {
251 t.Errorf("out of sync %d != %d", j, i)
252 }
253 }
254 })
255 }
256
257 func TestCallbackInAnotherThread(t *testing.T) {
258 d := GetDLL(t, "kernel32.dll")
259
260 f := func(p uintptr) uintptr {
261 return p
262 }
263 r, _, err := d.Proc("CreateThread").Call(0, 0, syscall.NewCallback(f), 123, 0, 0)
264 if r == 0 {
265 t.Fatalf("CreateThread failed: %v", err)
266 }
267 h := syscall.Handle(r)
268 defer syscall.CloseHandle(h)
269
270 switch s, err := syscall.WaitForSingleObject(h, syscall.INFINITE); s {
271 case syscall.WAIT_OBJECT_0:
272 break
273 case syscall.WAIT_FAILED:
274 t.Fatalf("WaitForSingleObject failed: %v", err)
275 default:
276 t.Fatalf("WaitForSingleObject returns unexpected value %v", s)
277 }
278
279 var ec uint32
280 r, _, err = d.Proc("GetExitCodeThread").Call(uintptr(h), uintptr(unsafe.Pointer(&ec)))
281 if r == 0 {
282 t.Fatalf("GetExitCodeThread failed: %v", err)
283 }
284 if ec != 123 {
285 t.Fatalf("expected 123, but got %d", ec)
286 }
287 }
288
289 type cbFunc struct {
290 goFunc any
291 }
292
293 func (f cbFunc) cName(cdecl bool) string {
294 name := "stdcall"
295 if cdecl {
296 name = "cdecl"
297 }
298 t := reflect.TypeOf(f.goFunc)
299 for i := 0; i < t.NumIn(); i++ {
300 name += "_" + t.In(i).Name()
301 }
302 return name
303 }
304
305 func (f cbFunc) cSrc(w io.Writer, cdecl bool) {
306
307
308 funcname := f.cName(cdecl)
309 attr := "__stdcall"
310 if cdecl {
311 attr = "__cdecl"
312 }
313 typename := "t" + funcname
314 t := reflect.TypeOf(f.goFunc)
315 cTypes := make([]string, t.NumIn())
316 cArgs := make([]string, t.NumIn())
317 for i := range cTypes {
318
319
320 cTypes[i] = t.In(i).Name() + "_t"
321 if t.In(i).Name() == "uint8Pair" {
322 cArgs[i] = fmt.Sprintf("(uint8Pair_t){%d,1}", i)
323 } else {
324 cArgs[i] = fmt.Sprintf("%d", i+1)
325 }
326 }
327 fmt.Fprintf(w, `
328 typedef uintptr_t %s (*%s)(%s);
329 uintptr_t %s(%s f) {
330 return f(%s);
331 }
332 `, attr, typename, strings.Join(cTypes, ","), funcname, typename, strings.Join(cArgs, ","))
333 }
334
335 func (f cbFunc) testOne(t *testing.T, dll *syscall.DLL, cdecl bool, cb uintptr) {
336 r1, _, _ := dll.MustFindProc(f.cName(cdecl)).Call(cb)
337
338 want := 0
339 for i := 0; i < reflect.TypeOf(f.goFunc).NumIn(); i++ {
340 want += i + 1
341 }
342 if int(r1) != want {
343 t.Errorf("wanted result %d; got %d", want, r1)
344 }
345 }
346
347 type uint8Pair struct{ x, y uint8 }
348
349 var cbFuncs = []cbFunc{
350 {func(i1, i2 uintptr) uintptr {
351 return i1 + i2
352 }},
353 {func(i1, i2, i3 uintptr) uintptr {
354 return i1 + i2 + i3
355 }},
356 {func(i1, i2, i3, i4 uintptr) uintptr {
357 return i1 + i2 + i3 + i4
358 }},
359 {func(i1, i2, i3, i4, i5 uintptr) uintptr {
360 return i1 + i2 + i3 + i4 + i5
361 }},
362 {func(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
363 return i1 + i2 + i3 + i4 + i5 + i6
364 }},
365 {func(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
366 return i1 + i2 + i3 + i4 + i5 + i6 + i7
367 }},
368 {func(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
369 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8
370 }},
371 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr {
372 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9
373 }},
374
375
376 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint8) uintptr {
377 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
378 }},
379 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint16) uintptr {
380 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
381 }},
382 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 int8) uintptr {
383 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
384 }},
385 {func(i1 int8, i2 int16, i3 int32, i4, i5 uintptr) uintptr {
386 return uintptr(i1) + uintptr(i2) + uintptr(i3) + i4 + i5
387 }},
388 {func(i1, i2, i3, i4, i5 uint8Pair) uintptr {
389 return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y)
390 }},
391 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
392 runtime.GC()
393 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
394 }},
395 }
396
397
398 func sum2(i1, i2 uintptr) uintptr {
399 return i1 + i2
400 }
401
402
403 func sum3(i1, i2, i3 uintptr) uintptr {
404 return i1 + i2 + i3
405 }
406
407
408 func sum4(i1, i2, i3, i4 uintptr) uintptr {
409 return i1 + i2 + i3 + i4
410 }
411
412
413 func sum5(i1, i2, i3, i4, i5 uintptr) uintptr {
414 return i1 + i2 + i3 + i4 + i5
415 }
416
417
418 func sum6(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
419 return i1 + i2 + i3 + i4 + i5 + i6
420 }
421
422
423 func sum7(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
424 return i1 + i2 + i3 + i4 + i5 + i6 + i7
425 }
426
427
428 func sum8(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
429 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8
430 }
431
432
433 func sum9(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr {
434 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9
435 }
436
437
438 func sum10(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10 uintptr) uintptr {
439 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10
440 }
441
442
443 func sum9uint8(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint8) uintptr {
444 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
445 }
446
447
448 func sum9uint16(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint16) uintptr {
449 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
450 }
451
452
453 func sum9int8(i1, i2, i3, i4, i5, i6, i7, i8, i9 int8) uintptr {
454 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
455 }
456
457
458 func sum5mix(i1 int8, i2 int16, i3 int32, i4, i5 uintptr) uintptr {
459 return uintptr(i1) + uintptr(i2) + uintptr(i3) + i4 + i5
460 }
461
462
463 func sum5andPair(i1, i2, i3, i4, i5 uint8Pair) uintptr {
464 return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y)
465 }
466
467
468
469
470
471
472
473 func sum9andGC(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
474 runtime.GC()
475 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
476 }
477
478
479
480
481 var cbFuncsRegABI = []cbFunc{
482 {sum2},
483 {sum3},
484 {sum4},
485 {sum5},
486 {sum6},
487 {sum7},
488 {sum8},
489 {sum9},
490 {sum10},
491 {sum9uint8},
492 {sum9uint16},
493 {sum9int8},
494 {sum5mix},
495 {sum5andPair},
496 {sum9andGC},
497 }
498
499 func getCallbackTestFuncs() []cbFunc {
500 if regs := runtime.SetIntArgRegs(-1); regs > 0 {
501 return cbFuncsRegABI
502 }
503 return cbFuncs
504 }
505
506 type cbDLL struct {
507 name string
508 buildArgs func(out, src string) []string
509 }
510
511 func (d *cbDLL) makeSrc(t *testing.T, path string) {
512 f, err := os.Create(path)
513 if err != nil {
514 t.Fatalf("failed to create source file: %v", err)
515 }
516 defer f.Close()
517
518 fmt.Fprint(f, `
519 #include <stdint.h>
520 typedef struct { uint8_t x, y; } uint8Pair_t;
521 `)
522 for _, cbf := range getCallbackTestFuncs() {
523 cbf.cSrc(f, false)
524 cbf.cSrc(f, true)
525 }
526 }
527
528 func (d *cbDLL) build(t *testing.T, dir string) string {
529 srcname := d.name + ".c"
530 d.makeSrc(t, filepath.Join(dir, srcname))
531 outname := d.name + ".dll"
532 args := d.buildArgs(outname, srcname)
533 cmd := exec.Command(args[0], args[1:]...)
534 cmd.Dir = dir
535 out, err := cmd.CombinedOutput()
536 if err != nil {
537 t.Fatalf("failed to build dll: %v - %v", err, string(out))
538 }
539 return filepath.Join(dir, outname)
540 }
541
542 var cbDLLs = []cbDLL{
543 {
544 "test",
545 func(out, src string) []string {
546 return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, src}
547 },
548 },
549 {
550 "testO2",
551 func(out, src string) []string {
552 return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, "-O2", src}
553 },
554 },
555 }
556
557 func TestStdcallAndCDeclCallbacks(t *testing.T) {
558 if _, err := exec.LookPath("gcc"); err != nil {
559 t.Skip("skipping test: gcc is missing")
560 }
561 tmp := t.TempDir()
562
563 oldRegs := runtime.SetIntArgRegs(abi.IntArgRegs)
564 defer runtime.SetIntArgRegs(oldRegs)
565
566 for _, dll := range cbDLLs {
567 t.Run(dll.name, func(t *testing.T) {
568 dllPath := dll.build(t, tmp)
569 dll := syscall.MustLoadDLL(dllPath)
570 defer dll.Release()
571 for _, cbf := range getCallbackTestFuncs() {
572 t.Run(cbf.cName(false), func(t *testing.T) {
573 stdcall := syscall.NewCallback(cbf.goFunc)
574 cbf.testOne(t, dll, false, stdcall)
575 })
576 t.Run(cbf.cName(true), func(t *testing.T) {
577 cdecl := syscall.NewCallbackCDecl(cbf.goFunc)
578 cbf.testOne(t, dll, true, cdecl)
579 })
580 }
581 })
582 }
583 }
584
585 func TestRegisterClass(t *testing.T) {
586 kernel32 := GetDLL(t, "kernel32.dll")
587 user32 := GetDLL(t, "user32.dll")
588 mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0)
589 cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
590 t.Fatal("callback should never get called")
591 return 0
592 })
593 type Wndclassex struct {
594 Size uint32
595 Style uint32
596 WndProc uintptr
597 ClsExtra int32
598 WndExtra int32
599 Instance syscall.Handle
600 Icon syscall.Handle
601 Cursor syscall.Handle
602 Background syscall.Handle
603 MenuName *uint16
604 ClassName *uint16
605 IconSm syscall.Handle
606 }
607 name := syscall.StringToUTF16Ptr("test_window")
608 wc := Wndclassex{
609 WndProc: cb,
610 Instance: syscall.Handle(mh),
611 ClassName: name,
612 }
613 wc.Size = uint32(unsafe.Sizeof(wc))
614 a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc)))
615 if a == 0 {
616 t.Fatalf("RegisterClassEx failed: %v", err)
617 }
618 r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0)
619 if r == 0 {
620 t.Fatalf("UnregisterClass failed: %v", err)
621 }
622 }
623
624 func TestOutputDebugString(t *testing.T) {
625 d := GetDLL(t, "kernel32.dll")
626 p := syscall.StringToUTF16Ptr("testing OutputDebugString")
627 d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p)))
628 }
629
630 func TestRaiseException(t *testing.T) {
631 if strings.HasPrefix(testenv.Builder(), "windows-amd64-2012") {
632 testenv.SkipFlaky(t, 49681)
633 }
634 o := runTestProg(t, "testprog", "RaiseException")
635 if strings.Contains(o, "RaiseException should not return") {
636 t.Fatalf("RaiseException did not crash program: %v", o)
637 }
638 if !strings.Contains(o, "Exception 0xbad") {
639 t.Fatalf("No stack trace: %v", o)
640 }
641 }
642
643 func TestZeroDivisionException(t *testing.T) {
644 o := runTestProg(t, "testprog", "ZeroDivisionException")
645 if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
646 t.Fatalf("No stack trace: %v", o)
647 }
648 }
649
650 func TestWERDialogue(t *testing.T) {
651 if os.Getenv("TEST_WER_DIALOGUE") == "1" {
652 const EXCEPTION_NONCONTINUABLE = 1
653 mod := syscall.MustLoadDLL("kernel32.dll")
654 proc := mod.MustFindProc("RaiseException")
655 proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
656 t.Fatal("RaiseException should not return")
657 }
658 exe, err := os.Executable()
659 if err != nil {
660 t.Fatal(err)
661 }
662 cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, "-test.run=^TestWERDialogue$"))
663 cmd.Env = append(cmd.Env, "TEST_WER_DIALOGUE=1", "GOTRACEBACK=wer")
664
665
666 _, err = cmd.CombinedOutput()
667 if err == nil {
668 t.Error("test program succeeded unexpectedly")
669 }
670 }
671
672 func TestWindowsStackMemory(t *testing.T) {
673 if race.Enabled {
674 t.Skip("skipping test: race mode uses more stack memory")
675 }
676 o := runTestProg(t, "testprog", "StackMemory")
677 stackUsage, err := strconv.Atoi(o)
678 if err != nil {
679 t.Fatalf("Failed to read stack usage: %v", err)
680 }
681 if expected, got := 128<<10, stackUsage; got > expected {
682 t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
683 }
684 }
685
686 var used byte
687
688 func use(buf []byte) {
689 for _, c := range buf {
690 used += c
691 }
692 }
693
694 func forceStackCopy() (r int) {
695 var f func(int) int
696 f = func(i int) int {
697 var buf [256]byte
698 use(buf[:])
699 if i == 0 {
700 return 0
701 }
702 return i + f(i-1)
703 }
704 r = f(128)
705 return
706 }
707
708 func TestReturnAfterStackGrowInCallback(t *testing.T) {
709 if _, err := exec.LookPath("gcc"); err != nil {
710 t.Skip("skipping test: gcc is missing")
711 }
712
713 const src = `
714 #include <stdint.h>
715 #include <windows.h>
716
717 typedef uintptr_t __stdcall (*callback)(uintptr_t);
718
719 uintptr_t cfunc(callback f, uintptr_t n) {
720 uintptr_t r;
721 r = f(n);
722 SetLastError(333);
723 return r;
724 }
725 `
726 tmpdir := t.TempDir()
727
728 srcname := "mydll.c"
729 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
730 if err != nil {
731 t.Fatal(err)
732 }
733 outname := "mydll.dll"
734 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
735 cmd.Dir = tmpdir
736 out, err := cmd.CombinedOutput()
737 if err != nil {
738 t.Fatalf("failed to build dll: %v - %v", err, string(out))
739 }
740 dllpath := filepath.Join(tmpdir, outname)
741
742 dll := syscall.MustLoadDLL(dllpath)
743 defer dll.Release()
744
745 proc := dll.MustFindProc("cfunc")
746
747 cb := syscall.NewCallback(func(n uintptr) uintptr {
748 forceStackCopy()
749 return n
750 })
751
752
753 type result struct {
754 r uintptr
755 err syscall.Errno
756 }
757 want := result{
758
759 r: (^uintptr(0)) >> 24,
760 err: 333,
761 }
762 c := make(chan result)
763 go func() {
764 r, _, err := proc.Call(cb, want.r)
765 c <- result{r, err.(syscall.Errno)}
766 }()
767 if got := <-c; got != want {
768 t.Errorf("got %d want %d", got, want)
769 }
770 }
771
772 func TestSyscallN(t *testing.T) {
773 if _, err := exec.LookPath("gcc"); err != nil {
774 t.Skip("skipping test: gcc is missing")
775 }
776 if runtime.GOARCH != "amd64" {
777 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
778 }
779
780 for arglen := 0; arglen <= windows.MaxArgs; arglen++ {
781 arglen := arglen
782 t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) {
783 t.Parallel()
784 args := make([]string, arglen)
785 rets := make([]string, arglen+1)
786 params := make([]uintptr, arglen)
787 for i := range args {
788 args[i] = fmt.Sprintf("int a%d", i)
789 rets[i] = fmt.Sprintf("(a%d == %d)", i, i)
790 params[i] = uintptr(i)
791 }
792 rets[arglen] = "1"
793
794 src := fmt.Sprintf(`
795 #include <stdint.h>
796 #include <windows.h>
797 int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && "))
798
799 tmpdir := t.TempDir()
800
801 srcname := "mydll.c"
802 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
803 if err != nil {
804 t.Fatal(err)
805 }
806 outname := "mydll.dll"
807 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
808 cmd.Dir = tmpdir
809 out, err := cmd.CombinedOutput()
810 if err != nil {
811 t.Fatalf("failed to build dll: %v\n%s", err, out)
812 }
813 dllpath := filepath.Join(tmpdir, outname)
814
815 dll := syscall.MustLoadDLL(dllpath)
816 defer dll.Release()
817
818 proc := dll.MustFindProc("cfunc")
819
820
821 r, _, err := proc.Call(params...)
822 if r != 1 {
823 t.Errorf("got %d want 1 (err=%v)", r, err)
824 }
825 })
826 }
827 }
828
829 func TestFloatArgs(t *testing.T) {
830 if _, err := exec.LookPath("gcc"); err != nil {
831 t.Skip("skipping test: gcc is missing")
832 }
833 if runtime.GOARCH != "amd64" {
834 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
835 }
836
837 const src = `
838 #include <stdint.h>
839 #include <windows.h>
840
841 uintptr_t cfunc(uintptr_t a, double b, float c, double d) {
842 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
843 return 1;
844 }
845 return 0;
846 }
847 `
848 tmpdir := t.TempDir()
849
850 srcname := "mydll.c"
851 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
852 if err != nil {
853 t.Fatal(err)
854 }
855 outname := "mydll.dll"
856 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
857 cmd.Dir = tmpdir
858 out, err := cmd.CombinedOutput()
859 if err != nil {
860 t.Fatalf("failed to build dll: %v - %v", err, string(out))
861 }
862 dllpath := filepath.Join(tmpdir, outname)
863
864 dll := syscall.MustLoadDLL(dllpath)
865 defer dll.Release()
866
867 proc := dll.MustFindProc("cfunc")
868
869 r, _, err := proc.Call(
870 1,
871 uintptr(math.Float64bits(2.2)),
872 uintptr(math.Float32bits(3.3)),
873 uintptr(math.Float64bits(4.4e44)),
874 )
875 if r != 1 {
876 t.Errorf("got %d want 1 (err=%v)", r, err)
877 }
878 }
879
880 func TestFloatReturn(t *testing.T) {
881 if _, err := exec.LookPath("gcc"); err != nil {
882 t.Skip("skipping test: gcc is missing")
883 }
884 if runtime.GOARCH != "amd64" {
885 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
886 }
887
888 const src = `
889 #include <stdint.h>
890 #include <windows.h>
891
892 float cfuncFloat(uintptr_t a, double b, float c, double d) {
893 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
894 return 1.5f;
895 }
896 return 0;
897 }
898
899 double cfuncDouble(uintptr_t a, double b, float c, double d) {
900 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
901 return 2.5;
902 }
903 return 0;
904 }
905 `
906 tmpdir := t.TempDir()
907
908 srcname := "mydll.c"
909 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
910 if err != nil {
911 t.Fatal(err)
912 }
913 outname := "mydll.dll"
914 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
915 cmd.Dir = tmpdir
916 out, err := cmd.CombinedOutput()
917 if err != nil {
918 t.Fatalf("failed to build dll: %v - %v", err, string(out))
919 }
920 dllpath := filepath.Join(tmpdir, outname)
921
922 dll := syscall.MustLoadDLL(dllpath)
923 defer dll.Release()
924
925 proc := dll.MustFindProc("cfuncFloat")
926
927 _, r, err := proc.Call(
928 1,
929 uintptr(math.Float64bits(2.2)),
930 uintptr(math.Float32bits(3.3)),
931 uintptr(math.Float64bits(4.4e44)),
932 )
933 fr := math.Float32frombits(uint32(r))
934 if fr != 1.5 {
935 t.Errorf("got %f want 1.5 (err=%v)", fr, err)
936 }
937
938 proc = dll.MustFindProc("cfuncDouble")
939
940 _, r, err = proc.Call(
941 1,
942 uintptr(math.Float64bits(2.2)),
943 uintptr(math.Float32bits(3.3)),
944 uintptr(math.Float64bits(4.4e44)),
945 )
946 dr := math.Float64frombits(uint64(r))
947 if dr != 2.5 {
948 t.Errorf("got %f want 2.5 (err=%v)", dr, err)
949 }
950 }
951
952 func TestTimeBeginPeriod(t *testing.T) {
953 const TIMERR_NOERROR = 0
954 if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR {
955 t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue)
956 }
957 }
958
959
960
961 func removeOneCPU(mask uintptr) (uintptr, error) {
962 if mask == 0 {
963 return 0, fmt.Errorf("cpu affinity mask is empty")
964 }
965 maskbits := int(unsafe.Sizeof(mask) * 8)
966 for i := 0; i < maskbits; i++ {
967 newmask := mask & ^(1 << uint(i))
968 if newmask != mask {
969 return newmask, nil
970 }
971
972 }
973 panic("not reached")
974 }
975
976 func resumeChildThread(kernel32 *syscall.DLL, childpid int) error {
977 _OpenThread := kernel32.MustFindProc("OpenThread")
978 _ResumeThread := kernel32.MustFindProc("ResumeThread")
979 _Thread32First := kernel32.MustFindProc("Thread32First")
980 _Thread32Next := kernel32.MustFindProc("Thread32Next")
981
982 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0)
983 if err != nil {
984 return err
985 }
986 defer syscall.CloseHandle(snapshot)
987
988 const _THREAD_SUSPEND_RESUME = 0x0002
989
990 type ThreadEntry32 struct {
991 Size uint32
992 tUsage uint32
993 ThreadID uint32
994 OwnerProcessID uint32
995 BasePri int32
996 DeltaPri int32
997 Flags uint32
998 }
999
1000 var te ThreadEntry32
1001 te.Size = uint32(unsafe.Sizeof(te))
1002 ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
1003 if ret == 0 {
1004 return err
1005 }
1006 for te.OwnerProcessID != uint32(childpid) {
1007 ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
1008 if ret == 0 {
1009 return err
1010 }
1011 }
1012 h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID))
1013 if h == 0 {
1014 return err
1015 }
1016 defer syscall.Close(syscall.Handle(h))
1017
1018 ret, _, err = _ResumeThread.Call(h)
1019 if ret == 0xffffffff {
1020 return err
1021 }
1022 return nil
1023 }
1024
1025 func TestNumCPU(t *testing.T) {
1026 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1027
1028 fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU())
1029 os.Exit(0)
1030 }
1031
1032 switch n := runtime.NumberOfProcessors(); {
1033 case n < 1:
1034 t.Fatalf("system cannot have %d cpu(s)", n)
1035 case n == 1:
1036 if runtime.NumCPU() != 1 {
1037 t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU())
1038 }
1039 return
1040 }
1041
1042 const (
1043 _CREATE_SUSPENDED = 0x00000004
1044 _PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
1045 )
1046
1047 kernel32 := syscall.MustLoadDLL("kernel32.dll")
1048 _GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask")
1049 _SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask")
1050
1051 cmd := exec.Command(testenv.Executable(t), "-test.run=^TestNumCPU$")
1052 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
1053 var buf strings.Builder
1054 cmd.Stdout = &buf
1055 cmd.Stderr = &buf
1056 cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED}
1057 err := cmd.Start()
1058 if err != nil {
1059 t.Fatal(err)
1060 }
1061 defer func() {
1062 err = cmd.Wait()
1063 childOutput := buf.String()
1064 if err != nil {
1065 t.Fatalf("child failed: %v: %v", err, childOutput)
1066 }
1067
1068 want := fmt.Sprintf("%d", runtime.NumCPU()-1)
1069 if childOutput != want {
1070 t.Fatalf("child output: want %q, got %q", want, childOutput)
1071 }
1072 }()
1073
1074 defer func() {
1075 err = resumeChildThread(kernel32, cmd.Process.Pid)
1076 if err != nil {
1077 t.Fatal(err)
1078 }
1079 }()
1080
1081 ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid))
1082 if err != nil {
1083 t.Fatal(err)
1084 }
1085 defer syscall.CloseHandle(ph)
1086
1087 var mask, sysmask uintptr
1088 ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
1089 if ret == 0 {
1090 t.Fatal(err)
1091 }
1092
1093 newmask, err := removeOneCPU(mask)
1094 if err != nil {
1095 t.Fatal(err)
1096 }
1097
1098 ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask)
1099 if ret == 0 {
1100 t.Fatal(err)
1101 }
1102 ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
1103 if ret == 0 {
1104 t.Fatal(err)
1105 }
1106 if newmask != mask {
1107 t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
1108 }
1109 }
1110
1111
1112 func TestDLLPreloadMitigation(t *testing.T) {
1113 if _, err := exec.LookPath("gcc"); err != nil {
1114 t.Skip("skipping test: gcc is missing")
1115 }
1116
1117 tmpdir := t.TempDir()
1118
1119 const src = `
1120 #include <stdint.h>
1121 #include <windows.h>
1122
1123 uintptr_t cfunc(void) {
1124 SetLastError(123);
1125 return 0;
1126 }
1127 `
1128 srcname := "nojack.c"
1129 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
1130 if err != nil {
1131 t.Fatal(err)
1132 }
1133 name := "nojack.dll"
1134 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
1135 cmd.Dir = tmpdir
1136 out, err := cmd.CombinedOutput()
1137 if err != nil {
1138 t.Fatalf("failed to build dll: %v - %v", err, string(out))
1139 }
1140 dllpath := filepath.Join(tmpdir, name)
1141
1142 dll := syscall.MustLoadDLL(dllpath)
1143 dll.MustFindProc("cfunc")
1144 dll.Release()
1145
1146
1147
1148
1149
1150 t.Chdir(tmpdir)
1151
1152
1153
1154 delete(sysdll.IsSystemDLL, name)
1155 dll, err = syscall.LoadDLL(name)
1156 if err != nil {
1157 t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
1158 }
1159 dll.Release()
1160
1161
1162
1163
1164 sysdll.IsSystemDLL[name] = true
1165 dll, err = syscall.LoadDLL(name)
1166 if err == nil {
1167 dll.Release()
1168 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
1169 }
1170 }
1171
1172
1173
1174
1175
1176 func TestBigStackCallbackSyscall(t *testing.T) {
1177 if _, err := exec.LookPath("gcc"); err != nil {
1178 t.Skip("skipping test: gcc is missing")
1179 }
1180
1181 srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c")
1182 if err != nil {
1183 t.Fatal("Abs failed: ", err)
1184 }
1185
1186 tmpdir := t.TempDir()
1187
1188 outname := "mydll.dll"
1189 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
1190 cmd.Dir = tmpdir
1191 out, err := cmd.CombinedOutput()
1192 if err != nil {
1193 t.Fatalf("failed to build dll: %v - %v", err, string(out))
1194 }
1195 dllpath := filepath.Join(tmpdir, outname)
1196
1197 dll := syscall.MustLoadDLL(dllpath)
1198 defer dll.Release()
1199
1200 var ok bool
1201 proc := dll.MustFindProc("bigStack")
1202 cb := syscall.NewCallback(func() uintptr {
1203
1204 forceStackCopy()
1205 ok = true
1206 return 0
1207 })
1208 proc.Call(cb)
1209 if !ok {
1210 t.Fatalf("callback not called")
1211 }
1212 }
1213
1214 func TestSyscallStackUsage(t *testing.T) {
1215
1216
1217 syscall.Syscall15(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
1218 syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
1219 }
1220
1221 var (
1222 modwinmm = syscall.NewLazyDLL("winmm.dll")
1223 modkernel32 = syscall.NewLazyDLL("kernel32.dll")
1224
1225 procCreateEvent = modkernel32.NewProc("CreateEventW")
1226 procSetEvent = modkernel32.NewProc("SetEvent")
1227 )
1228
1229 func createEvent() (syscall.Handle, error) {
1230 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
1231 if r0 == 0 {
1232 return 0, syscall.Errno(e0)
1233 }
1234 return syscall.Handle(r0), nil
1235 }
1236
1237 func setEvent(h syscall.Handle) error {
1238 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
1239 if r0 == 0 {
1240 return syscall.Errno(e0)
1241 }
1242 return nil
1243 }
1244
1245 func BenchmarkChanToSyscallPing(b *testing.B) {
1246 n := b.N
1247 ch := make(chan int)
1248 event, err := createEvent()
1249 if err != nil {
1250 b.Fatal(err)
1251 }
1252 go func() {
1253 for i := 0; i < n; i++ {
1254 syscall.WaitForSingleObject(event, syscall.INFINITE)
1255 ch <- 1
1256 }
1257 }()
1258 for i := 0; i < n; i++ {
1259 err := setEvent(event)
1260 if err != nil {
1261 b.Fatal(err)
1262 }
1263 <-ch
1264 }
1265 }
1266
1267 func BenchmarkSyscallToSyscallPing(b *testing.B) {
1268 n := b.N
1269 event1, err := createEvent()
1270 if err != nil {
1271 b.Fatal(err)
1272 }
1273 event2, err := createEvent()
1274 if err != nil {
1275 b.Fatal(err)
1276 }
1277 go func() {
1278 for i := 0; i < n; i++ {
1279 syscall.WaitForSingleObject(event1, syscall.INFINITE)
1280 if err := setEvent(event2); err != nil {
1281 b.Errorf("Set event failed: %v", err)
1282 return
1283 }
1284 }
1285 }()
1286 for i := 0; i < n; i++ {
1287 if err := setEvent(event1); err != nil {
1288 b.Fatal(err)
1289 }
1290 if b.Failed() {
1291 break
1292 }
1293 syscall.WaitForSingleObject(event2, syscall.INFINITE)
1294 }
1295 }
1296
1297 func BenchmarkChanToChanPing(b *testing.B) {
1298 n := b.N
1299 ch1 := make(chan int)
1300 ch2 := make(chan int)
1301 go func() {
1302 for i := 0; i < n; i++ {
1303 <-ch1
1304 ch2 <- 1
1305 }
1306 }()
1307 for i := 0; i < n; i++ {
1308 ch1 <- 1
1309 <-ch2
1310 }
1311 }
1312
1313 func BenchmarkOsYield(b *testing.B) {
1314 for i := 0; i < b.N; i++ {
1315 runtime.OsYield()
1316 }
1317 }
1318
1319 func BenchmarkRunningGoProgram(b *testing.B) {
1320 tmpdir := b.TempDir()
1321
1322 src := filepath.Join(tmpdir, "main.go")
1323 err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666)
1324 if err != nil {
1325 b.Fatal(err)
1326 }
1327
1328 exe := filepath.Join(tmpdir, "main.exe")
1329 cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src)
1330 cmd.Dir = tmpdir
1331 out, err := cmd.CombinedOutput()
1332 if err != nil {
1333 b.Fatalf("building main.exe failed: %v\n%s", err, out)
1334 }
1335
1336 b.ResetTimer()
1337 for i := 0; i < b.N; i++ {
1338 cmd := exec.Command(exe)
1339 out, err := cmd.CombinedOutput()
1340 if err != nil {
1341 b.Fatalf("running main.exe failed: %v\n%s", err, out)
1342 }
1343 }
1344 }
1345
1346 const benchmarkRunningGoProgram = `
1347 package main
1348
1349 import _ "os" // average Go program will use "os" package, do the same here
1350
1351 func main() {
1352 }
1353 `
1354
View as plain text