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