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