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/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 o := runTestProg(t, "testprog", "RaiseException")
632 if strings.Contains(o, "RaiseException should not return") {
633 t.Fatalf("RaiseException did not crash program: %v", o)
634 }
635 if !strings.Contains(o, "Exception 0xbad") {
636 t.Fatalf("No stack trace: %v", o)
637 }
638 }
639
640 func TestZeroDivisionException(t *testing.T) {
641 o := runTestProg(t, "testprog", "ZeroDivisionException")
642 if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
643 t.Fatalf("No stack trace: %v", o)
644 }
645 }
646
647 func TestWERDialogue(t *testing.T) {
648 if os.Getenv("TEST_WER_DIALOGUE") == "1" {
649 const EXCEPTION_NONCONTINUABLE = 1
650 mod := syscall.MustLoadDLL("kernel32.dll")
651 proc := mod.MustFindProc("RaiseException")
652 proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
653 t.Fatal("RaiseException should not return")
654 }
655 exe, err := os.Executable()
656 if err != nil {
657 t.Fatal(err)
658 }
659 cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, "-test.run=^TestWERDialogue$"))
660 cmd.Env = append(cmd.Env, "TEST_WER_DIALOGUE=1", "GOTRACEBACK=wer")
661
662
663 _, err = cmd.CombinedOutput()
664 if err == nil {
665 t.Error("test program succeeded unexpectedly")
666 }
667 }
668
669 func TestWindowsStackMemory(t *testing.T) {
670 if race.Enabled {
671 t.Skip("skipping test: race mode uses more stack memory")
672 }
673 o := runTestProg(t, "testprog", "StackMemory")
674 stackUsage, err := strconv.Atoi(o)
675 if err != nil {
676 t.Fatalf("Failed to read stack usage: %v", err)
677 }
678 if expected, got := 128<<10, stackUsage; got > expected {
679 t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
680 }
681 }
682
683 var used byte
684
685 func use(buf []byte) {
686 for _, c := range buf {
687 used += c
688 }
689 }
690
691 func forceStackCopy() (r int) {
692 var f func(int) int
693 f = func(i int) int {
694 var buf [256]byte
695 use(buf[:])
696 if i == 0 {
697 return 0
698 }
699 return i + f(i-1)
700 }
701 r = f(128)
702 return
703 }
704
705 func TestReturnAfterStackGrowInCallback(t *testing.T) {
706 if _, err := exec.LookPath("gcc"); err != nil {
707 t.Skip("skipping test: gcc is missing")
708 }
709
710 const src = `
711 #include <stdint.h>
712 #include <windows.h>
713
714 typedef uintptr_t __stdcall (*callback)(uintptr_t);
715
716 uintptr_t cfunc(callback f, uintptr_t n) {
717 uintptr_t r;
718 r = f(n);
719 SetLastError(333);
720 return r;
721 }
722 `
723 tmpdir := t.TempDir()
724
725 srcname := "mydll.c"
726 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
727 if err != nil {
728 t.Fatal(err)
729 }
730 outname := "mydll.dll"
731 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
732 cmd.Dir = tmpdir
733 out, err := cmd.CombinedOutput()
734 if err != nil {
735 t.Fatalf("failed to build dll: %v - %v", err, string(out))
736 }
737 dllpath := filepath.Join(tmpdir, outname)
738
739 dll := syscall.MustLoadDLL(dllpath)
740 defer dll.Release()
741
742 proc := dll.MustFindProc("cfunc")
743
744 cb := syscall.NewCallback(func(n uintptr) uintptr {
745 forceStackCopy()
746 return n
747 })
748
749
750 type result struct {
751 r uintptr
752 err syscall.Errno
753 }
754 want := result{
755
756 r: (^uintptr(0)) >> 24,
757 err: 333,
758 }
759 c := make(chan result)
760 go func() {
761 r, _, err := proc.Call(cb, want.r)
762 c <- result{r, err.(syscall.Errno)}
763 }()
764 if got := <-c; got != want {
765 t.Errorf("got %d want %d", got, want)
766 }
767 }
768
769 func TestSyscallN(t *testing.T) {
770 if _, err := exec.LookPath("gcc"); err != nil {
771 t.Skip("skipping test: gcc is missing")
772 }
773
774 var nargs = 64
775 if testing.Short() {
776 nargs = 16
777 }
778
779 for arglen := range nargs {
780 t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) {
781 t.Parallel()
782 args := make([]string, arglen)
783 rets := make([]string, arglen+1)
784 params := make([]uintptr, arglen)
785 for i := range args {
786 args[i] = fmt.Sprintf("int a%d", i)
787 rets[i] = fmt.Sprintf("(a%d == %d)", i, i)
788 params[i] = uintptr(i)
789 }
790 rets[arglen] = "1"
791
792 src := fmt.Sprintf(`
793 #include <stdint.h>
794 #include <windows.h>
795 int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && "))
796
797 tmpdir := t.TempDir()
798
799 srcname := "mydll.c"
800 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
801 if err != nil {
802 t.Fatal(err)
803 }
804 outname := "mydll.dll"
805 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
806 cmd.Dir = tmpdir
807 out, err := cmd.CombinedOutput()
808 if err != nil {
809 t.Fatalf("failed to build dll: %v\n%s", err, out)
810 }
811 dllpath := filepath.Join(tmpdir, outname)
812
813 dll := syscall.MustLoadDLL(dllpath)
814 defer dll.Release()
815
816 proc := dll.MustFindProc("cfunc")
817
818
819 r, _, err := proc.Call(params...)
820 if r != 1 {
821 t.Errorf("got %d want 1 (err=%v)", r, err)
822 }
823 })
824 }
825 }
826
827 func TestFloatArgs(t *testing.T) {
828 if _, err := exec.LookPath("gcc"); err != nil {
829 t.Skip("skipping test: gcc is missing")
830 }
831 if runtime.GOARCH != "amd64" {
832 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
833 }
834
835 const src = `
836 #include <stdint.h>
837 #include <windows.h>
838
839 uintptr_t cfunc(uintptr_t a, double b, float c, double d) {
840 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
841 return 1;
842 }
843 return 0;
844 }
845 `
846 tmpdir := t.TempDir()
847
848 srcname := "mydll.c"
849 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
850 if err != nil {
851 t.Fatal(err)
852 }
853 outname := "mydll.dll"
854 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
855 cmd.Dir = tmpdir
856 out, err := cmd.CombinedOutput()
857 if err != nil {
858 t.Fatalf("failed to build dll: %v - %v", err, string(out))
859 }
860 dllpath := filepath.Join(tmpdir, outname)
861
862 dll := syscall.MustLoadDLL(dllpath)
863 defer dll.Release()
864
865 proc := dll.MustFindProc("cfunc")
866
867 r, _, err := proc.Call(
868 1,
869 uintptr(math.Float64bits(2.2)),
870 uintptr(math.Float32bits(3.3)),
871 uintptr(math.Float64bits(4.4e44)),
872 )
873 if r != 1 {
874 t.Errorf("got %d want 1 (err=%v)", r, err)
875 }
876 }
877
878 func TestFloatReturn(t *testing.T) {
879 if _, err := exec.LookPath("gcc"); err != nil {
880 t.Skip("skipping test: gcc is missing")
881 }
882 if runtime.GOARCH != "amd64" {
883 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
884 }
885
886 const src = `
887 #include <stdint.h>
888 #include <windows.h>
889
890 float cfuncFloat(uintptr_t a, double b, float c, double d) {
891 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
892 return 1.5f;
893 }
894 return 0;
895 }
896
897 double cfuncDouble(uintptr_t a, double b, float c, double d) {
898 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
899 return 2.5;
900 }
901 return 0;
902 }
903 `
904 tmpdir := t.TempDir()
905
906 srcname := "mydll.c"
907 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
908 if err != nil {
909 t.Fatal(err)
910 }
911 outname := "mydll.dll"
912 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
913 cmd.Dir = tmpdir
914 out, err := cmd.CombinedOutput()
915 if err != nil {
916 t.Fatalf("failed to build dll: %v - %v", err, string(out))
917 }
918 dllpath := filepath.Join(tmpdir, outname)
919
920 dll := syscall.MustLoadDLL(dllpath)
921 defer dll.Release()
922
923 proc := dll.MustFindProc("cfuncFloat")
924
925 _, r, err := proc.Call(
926 1,
927 uintptr(math.Float64bits(2.2)),
928 uintptr(math.Float32bits(3.3)),
929 uintptr(math.Float64bits(4.4e44)),
930 )
931 fr := math.Float32frombits(uint32(r))
932 if fr != 1.5 {
933 t.Errorf("got %f want 1.5 (err=%v)", fr, err)
934 }
935
936 proc = dll.MustFindProc("cfuncDouble")
937
938 _, r, err = proc.Call(
939 1,
940 uintptr(math.Float64bits(2.2)),
941 uintptr(math.Float32bits(3.3)),
942 uintptr(math.Float64bits(4.4e44)),
943 )
944 dr := math.Float64frombits(uint64(r))
945 if dr != 2.5 {
946 t.Errorf("got %f want 2.5 (err=%v)", dr, err)
947 }
948 }
949
950 func TestTimeBeginPeriod(t *testing.T) {
951 const TIMERR_NOERROR = 0
952 if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR {
953 t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue)
954 }
955 }
956
957
958
959 func removeOneCPU(mask uintptr) (uintptr, error) {
960 if mask == 0 {
961 return 0, fmt.Errorf("cpu affinity mask is empty")
962 }
963 maskbits := int(unsafe.Sizeof(mask) * 8)
964 for i := 0; i < maskbits; i++ {
965 newmask := mask & ^(1 << uint(i))
966 if newmask != mask {
967 return newmask, nil
968 }
969
970 }
971 panic("not reached")
972 }
973
974 func resumeChildThread(kernel32 *syscall.DLL, childpid int) error {
975 _OpenThread := kernel32.MustFindProc("OpenThread")
976 _ResumeThread := kernel32.MustFindProc("ResumeThread")
977 _Thread32First := kernel32.MustFindProc("Thread32First")
978 _Thread32Next := kernel32.MustFindProc("Thread32Next")
979
980 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0)
981 if err != nil {
982 return err
983 }
984 defer syscall.CloseHandle(snapshot)
985
986 const _THREAD_SUSPEND_RESUME = 0x0002
987
988 type ThreadEntry32 struct {
989 Size uint32
990 tUsage uint32
991 ThreadID uint32
992 OwnerProcessID uint32
993 BasePri int32
994 DeltaPri int32
995 Flags uint32
996 }
997
998 var te ThreadEntry32
999 te.Size = uint32(unsafe.Sizeof(te))
1000 ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
1001 if ret == 0 {
1002 return err
1003 }
1004 for te.OwnerProcessID != uint32(childpid) {
1005 ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
1006 if ret == 0 {
1007 return err
1008 }
1009 }
1010 h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID))
1011 if h == 0 {
1012 return err
1013 }
1014 defer syscall.Close(syscall.Handle(h))
1015
1016 ret, _, err = _ResumeThread.Call(h)
1017 if ret == 0xffffffff {
1018 return err
1019 }
1020 return nil
1021 }
1022
1023 func TestNumCPU(t *testing.T) {
1024 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1025
1026 fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU())
1027 os.Exit(0)
1028 }
1029
1030 switch n := runtime.NumberOfProcessors(); {
1031 case n < 1:
1032 t.Fatalf("system cannot have %d cpu(s)", n)
1033 case n == 1:
1034 if runtime.NumCPU() != 1 {
1035 t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU())
1036 }
1037 return
1038 }
1039
1040 const (
1041 _CREATE_SUSPENDED = 0x00000004
1042 _PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
1043 )
1044
1045 kernel32 := syscall.MustLoadDLL("kernel32.dll")
1046 _GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask")
1047 _SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask")
1048
1049 cmd := exec.Command(testenv.Executable(t), "-test.run=^TestNumCPU$")
1050 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
1051 var buf strings.Builder
1052 cmd.Stdout = &buf
1053 cmd.Stderr = &buf
1054 cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED}
1055 err := cmd.Start()
1056 if err != nil {
1057 t.Fatal(err)
1058 }
1059 defer func() {
1060 err = cmd.Wait()
1061 childOutput := buf.String()
1062 if err != nil {
1063 t.Fatalf("child failed: %v: %v", err, childOutput)
1064 }
1065
1066 want := fmt.Sprintf("%d", runtime.NumCPU()-1)
1067 if childOutput != want {
1068 t.Fatalf("child output: want %q, got %q", want, childOutput)
1069 }
1070 }()
1071
1072 defer func() {
1073 err = resumeChildThread(kernel32, cmd.Process.Pid)
1074 if err != nil {
1075 t.Fatal(err)
1076 }
1077 }()
1078
1079 ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid))
1080 if err != nil {
1081 t.Fatal(err)
1082 }
1083 defer syscall.CloseHandle(ph)
1084
1085 var mask, sysmask uintptr
1086 ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
1087 if ret == 0 {
1088 t.Fatal(err)
1089 }
1090
1091 newmask, err := removeOneCPU(mask)
1092 if err != nil {
1093 t.Fatal(err)
1094 }
1095
1096 ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask)
1097 if ret == 0 {
1098 t.Fatal(err)
1099 }
1100 ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
1101 if ret == 0 {
1102 t.Fatal(err)
1103 }
1104 if newmask != mask {
1105 t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
1106 }
1107 }
1108
1109
1110 func TestDLLPreloadMitigation(t *testing.T) {
1111 if _, err := exec.LookPath("gcc"); err != nil {
1112 t.Skip("skipping test: gcc is missing")
1113 }
1114
1115 tmpdir := t.TempDir()
1116
1117 const src = `
1118 #include <stdint.h>
1119 #include <windows.h>
1120
1121 uintptr_t cfunc(void) {
1122 SetLastError(123);
1123 return 0;
1124 }
1125 `
1126 srcname := "nojack.c"
1127 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
1128 if err != nil {
1129 t.Fatal(err)
1130 }
1131 name := "nojack.dll"
1132 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
1133 cmd.Dir = tmpdir
1134 out, err := cmd.CombinedOutput()
1135 if err != nil {
1136 t.Fatalf("failed to build dll: %v - %v", err, string(out))
1137 }
1138 dllpath := filepath.Join(tmpdir, name)
1139
1140 dll := syscall.MustLoadDLL(dllpath)
1141 dll.MustFindProc("cfunc")
1142 dll.Release()
1143
1144
1145
1146
1147
1148 t.Chdir(tmpdir)
1149
1150
1151
1152 delete(sysdll.IsSystemDLL, name)
1153 dll, err = syscall.LoadDLL(name)
1154 if err != nil {
1155 t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
1156 }
1157 dll.Release()
1158
1159
1160
1161
1162 sysdll.IsSystemDLL[name] = true
1163 dll, err = syscall.LoadDLL(name)
1164 if err == nil {
1165 dll.Release()
1166 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
1167 }
1168 }
1169
1170
1171
1172
1173
1174 func TestBigStackCallbackSyscall(t *testing.T) {
1175 if _, err := exec.LookPath("gcc"); err != nil {
1176 t.Skip("skipping test: gcc is missing")
1177 }
1178
1179 srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c")
1180 if err != nil {
1181 t.Fatal("Abs failed: ", err)
1182 }
1183
1184 tmpdir := t.TempDir()
1185
1186 outname := "mydll.dll"
1187 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
1188 cmd.Dir = tmpdir
1189 out, err := cmd.CombinedOutput()
1190 if err != nil {
1191 t.Fatalf("failed to build dll: %v - %v", err, string(out))
1192 }
1193 dllpath := filepath.Join(tmpdir, outname)
1194
1195 dll := syscall.MustLoadDLL(dllpath)
1196 defer dll.Release()
1197
1198 var ok bool
1199 proc := dll.MustFindProc("bigStack")
1200 cb := syscall.NewCallback(func() uintptr {
1201
1202 forceStackCopy()
1203 ok = true
1204 return 0
1205 })
1206 proc.Call(cb)
1207 if !ok {
1208 t.Fatalf("callback not called")
1209 }
1210 }
1211
1212 func TestSyscallStackUsage(t *testing.T) {
1213
1214
1215 syscall.Syscall15(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
1216 syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
1217 }
1218
1219 var (
1220 modwinmm = syscall.NewLazyDLL("winmm.dll")
1221 modkernel32 = syscall.NewLazyDLL("kernel32.dll")
1222
1223 procCreateEvent = modkernel32.NewProc("CreateEventW")
1224 procSetEvent = modkernel32.NewProc("SetEvent")
1225 )
1226
1227 func TestTrueVersion(t *testing.T) {
1228 ver, err := syscall.GetVersion()
1229 if err != nil {
1230 t.Fatalf("GetVersion failed: %v", err)
1231 }
1232 wantMajor, wantMinor, wantBuild := windows.Version()
1233 major := uint32(byte(ver))
1234 minor := uint32(uint8(ver >> 8))
1235 build := uint32(uint16(ver >> 16))
1236 if major != wantMajor || minor != wantMinor || build != wantBuild {
1237 t.Errorf("GetVersion = %d.%d (Build %d), want %d.%d (Build %d)", major, minor, build, wantMajor, wantMinor, wantBuild)
1238 }
1239 }
1240
1241 func createEvent() (syscall.Handle, error) {
1242 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
1243 if r0 == 0 {
1244 return 0, syscall.Errno(e0)
1245 }
1246 return syscall.Handle(r0), nil
1247 }
1248
1249 func setEvent(h syscall.Handle) error {
1250 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
1251 if r0 == 0 {
1252 return syscall.Errno(e0)
1253 }
1254 return nil
1255 }
1256
1257 func BenchmarkChanToSyscallPing(b *testing.B) {
1258 n := b.N
1259 ch := make(chan int)
1260 event, err := createEvent()
1261 if err != nil {
1262 b.Fatal(err)
1263 }
1264 go func() {
1265 for i := 0; i < n; i++ {
1266 syscall.WaitForSingleObject(event, syscall.INFINITE)
1267 ch <- 1
1268 }
1269 }()
1270 for i := 0; i < n; i++ {
1271 err := setEvent(event)
1272 if err != nil {
1273 b.Fatal(err)
1274 }
1275 <-ch
1276 }
1277 }
1278
1279 func BenchmarkSyscallToSyscallPing(b *testing.B) {
1280 n := b.N
1281 event1, err := createEvent()
1282 if err != nil {
1283 b.Fatal(err)
1284 }
1285 event2, err := createEvent()
1286 if err != nil {
1287 b.Fatal(err)
1288 }
1289 go func() {
1290 for i := 0; i < n; i++ {
1291 syscall.WaitForSingleObject(event1, syscall.INFINITE)
1292 if err := setEvent(event2); err != nil {
1293 b.Errorf("Set event failed: %v", err)
1294 return
1295 }
1296 }
1297 }()
1298 for i := 0; i < n; i++ {
1299 if err := setEvent(event1); err != nil {
1300 b.Fatal(err)
1301 }
1302 if b.Failed() {
1303 break
1304 }
1305 syscall.WaitForSingleObject(event2, syscall.INFINITE)
1306 }
1307 }
1308
1309 func BenchmarkChanToChanPing(b *testing.B) {
1310 n := b.N
1311 ch1 := make(chan int)
1312 ch2 := make(chan int)
1313 go func() {
1314 for i := 0; i < n; i++ {
1315 <-ch1
1316 ch2 <- 1
1317 }
1318 }()
1319 for i := 0; i < n; i++ {
1320 ch1 <- 1
1321 <-ch2
1322 }
1323 }
1324
1325 func BenchmarkOsYield(b *testing.B) {
1326 for i := 0; i < b.N; i++ {
1327 runtime.OsYield()
1328 }
1329 }
1330
1331 func BenchmarkRunningGoProgram(b *testing.B) {
1332 tmpdir := b.TempDir()
1333
1334 src := filepath.Join(tmpdir, "main.go")
1335 err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666)
1336 if err != nil {
1337 b.Fatal(err)
1338 }
1339
1340 exe := filepath.Join(tmpdir, "main.exe")
1341 cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src)
1342 cmd.Dir = tmpdir
1343 out, err := cmd.CombinedOutput()
1344 if err != nil {
1345 b.Fatalf("building main.exe failed: %v\n%s", err, out)
1346 }
1347
1348 b.ResetTimer()
1349 for i := 0; i < b.N; i++ {
1350 cmd := exec.Command(exe)
1351 out, err := cmd.CombinedOutput()
1352 if err != nil {
1353 b.Fatalf("running main.exe failed: %v\n%s", err, out)
1354 }
1355 }
1356 }
1357
1358 const benchmarkRunningGoProgram = `
1359 package main
1360
1361 import _ "os" // average Go program will use "os" package, do the same here
1362
1363 func main() {
1364 }
1365 `
1366
View as plain text