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