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