Source file
src/runtime/syscall_windows_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "fmt"
9 "internal/abi"
10 "internal/race"
11 "internal/syscall/windows/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, syscall.INFINITE); s {
270 case syscall.WAIT_OBJECT_0:
271 break
272 case syscall.WAIT_FAILED:
273 t.Fatalf("WaitForSingleObject failed: %v", err)
274 default:
275 t.Fatalf("WaitForSingleObject returns unexpected value %v", s)
276 }
277
278 var ec uint32
279 r, _, err = d.Proc("GetExitCodeThread").Call(uintptr(h), uintptr(unsafe.Pointer(&ec)))
280 if r == 0 {
281 t.Fatalf("GetExitCodeThread failed: %v", err)
282 }
283 if ec != 123 {
284 t.Fatalf("expected 123, but got %d", ec)
285 }
286 }
287
288 type cbFunc struct {
289 goFunc any
290 }
291
292 func (f cbFunc) cName(cdecl bool) string {
293 name := "stdcall"
294 if cdecl {
295 name = "cdecl"
296 }
297 t := reflect.TypeOf(f.goFunc)
298 for i := 0; i < t.NumIn(); i++ {
299 name += "_" + t.In(i).Name()
300 }
301 return name
302 }
303
304 func (f cbFunc) cSrc(w io.Writer, cdecl bool) {
305
306
307 funcname := f.cName(cdecl)
308 attr := "__stdcall"
309 if cdecl {
310 attr = "__cdecl"
311 }
312 typename := "t" + funcname
313 t := reflect.TypeOf(f.goFunc)
314 cTypes := make([]string, t.NumIn())
315 cArgs := make([]string, t.NumIn())
316 for i := range cTypes {
317
318
319 cTypes[i] = t.In(i).Name() + "_t"
320 if t.In(i).Name() == "uint8Pair" {
321 cArgs[i] = fmt.Sprintf("(uint8Pair_t){%d,1}", i)
322 } else {
323 cArgs[i] = fmt.Sprintf("%d", i+1)
324 }
325 }
326 fmt.Fprintf(w, `
327 typedef uintptr_t %s (*%s)(%s);
328 uintptr_t %s(%s f) {
329 return f(%s);
330 }
331 `, attr, typename, strings.Join(cTypes, ","), funcname, typename, strings.Join(cArgs, ","))
332 }
333
334 func (f cbFunc) testOne(t *testing.T, dll *syscall.DLL, cdecl bool, cb uintptr) {
335 r1, _, _ := dll.MustFindProc(f.cName(cdecl)).Call(cb)
336
337 want := 0
338 for i := 0; i < reflect.TypeOf(f.goFunc).NumIn(); i++ {
339 want += i + 1
340 }
341 if int(r1) != want {
342 t.Errorf("wanted result %d; got %d", want, r1)
343 }
344 }
345
346 type uint8Pair struct{ x, y uint8 }
347
348 var cbFuncs = []cbFunc{
349 {func(i1, i2 uintptr) uintptr {
350 return i1 + i2
351 }},
352 {func(i1, i2, i3 uintptr) uintptr {
353 return i1 + i2 + i3
354 }},
355 {func(i1, i2, i3, i4 uintptr) uintptr {
356 return i1 + i2 + i3 + i4
357 }},
358 {func(i1, i2, i3, i4, i5 uintptr) uintptr {
359 return i1 + i2 + i3 + i4 + i5
360 }},
361 {func(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
362 return i1 + i2 + i3 + i4 + i5 + i6
363 }},
364 {func(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
365 return i1 + i2 + i3 + i4 + i5 + i6 + i7
366 }},
367 {func(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
368 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8
369 }},
370 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr {
371 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9
372 }},
373
374
375 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint8) uintptr {
376 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
377 }},
378 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint16) uintptr {
379 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
380 }},
381 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 int8) uintptr {
382 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
383 }},
384 {func(i1 int8, i2 int16, i3 int32, i4, i5 uintptr) uintptr {
385 return uintptr(i1) + uintptr(i2) + uintptr(i3) + i4 + i5
386 }},
387 {func(i1, i2, i3, i4, i5 uint8Pair) uintptr {
388 return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y)
389 }},
390 {func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
391 runtime.GC()
392 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
393 }},
394 }
395
396
397 func sum2(i1, i2 uintptr) uintptr {
398 return i1 + i2
399 }
400
401
402 func sum3(i1, i2, i3 uintptr) uintptr {
403 return i1 + i2 + i3
404 }
405
406
407 func sum4(i1, i2, i3, i4 uintptr) uintptr {
408 return i1 + i2 + i3 + i4
409 }
410
411
412 func sum5(i1, i2, i3, i4, i5 uintptr) uintptr {
413 return i1 + i2 + i3 + i4 + i5
414 }
415
416
417 func sum6(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
418 return i1 + i2 + i3 + i4 + i5 + i6
419 }
420
421
422 func sum7(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
423 return i1 + i2 + i3 + i4 + i5 + i6 + i7
424 }
425
426
427 func sum8(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
428 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8
429 }
430
431
432 func sum9(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr {
433 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9
434 }
435
436
437 func sum10(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10 uintptr) uintptr {
438 return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10
439 }
440
441
442 func sum9uint8(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint8) uintptr {
443 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
444 }
445
446
447 func sum9uint16(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint16) uintptr {
448 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
449 }
450
451
452 func sum9int8(i1, i2, i3, i4, i5, i6, i7, i8, i9 int8) uintptr {
453 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
454 }
455
456
457 func sum5mix(i1 int8, i2 int16, i3 int32, i4, i5 uintptr) uintptr {
458 return uintptr(i1) + uintptr(i2) + uintptr(i3) + i4 + i5
459 }
460
461
462 func sum5andPair(i1, i2, i3, i4, i5 uint8Pair) uintptr {
463 return uintptr(i1.x + i1.y + i2.x + i2.y + i3.x + i3.y + i4.x + i4.y + i5.x + i5.y)
464 }
465
466
467
468
469
470
471
472 func sum9andGC(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
473 runtime.GC()
474 return uintptr(i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9)
475 }
476
477
478
479
480 var cbFuncsRegABI = []cbFunc{
481 {sum2},
482 {sum3},
483 {sum4},
484 {sum5},
485 {sum6},
486 {sum7},
487 {sum8},
488 {sum9},
489 {sum10},
490 {sum9uint8},
491 {sum9uint16},
492 {sum9int8},
493 {sum5mix},
494 {sum5andPair},
495 {sum9andGC},
496 }
497
498 func getCallbackTestFuncs() []cbFunc {
499 if regs := runtime.SetIntArgRegs(-1); regs > 0 {
500 return cbFuncsRegABI
501 }
502 return cbFuncs
503 }
504
505 type cbDLL struct {
506 name string
507 buildArgs func(out, src string) []string
508 }
509
510 func (d *cbDLL) makeSrc(t *testing.T, path string) {
511 f, err := os.Create(path)
512 if err != nil {
513 t.Fatalf("failed to create source file: %v", err)
514 }
515 defer f.Close()
516
517 fmt.Fprint(f, `
518 #include <stdint.h>
519 typedef struct { uint8_t x, y; } uint8Pair_t;
520 `)
521 for _, cbf := range getCallbackTestFuncs() {
522 cbf.cSrc(f, false)
523 cbf.cSrc(f, true)
524 }
525 }
526
527 func (d *cbDLL) build(t *testing.T, dir string) string {
528 srcname := d.name + ".c"
529 d.makeSrc(t, filepath.Join(dir, srcname))
530 outname := d.name + ".dll"
531 args := d.buildArgs(outname, srcname)
532 cmd := exec.Command(args[0], args[1:]...)
533 cmd.Dir = dir
534 out, err := cmd.CombinedOutput()
535 if err != nil {
536 t.Fatalf("failed to build dll: %v - %v", err, string(out))
537 }
538 return filepath.Join(dir, outname)
539 }
540
541 var cbDLLs = []cbDLL{
542 {
543 "test",
544 func(out, src string) []string {
545 return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, src}
546 },
547 },
548 {
549 "testO2",
550 func(out, src string) []string {
551 return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, "-O2", src}
552 },
553 },
554 }
555
556 func TestStdcallAndCDeclCallbacks(t *testing.T) {
557 if _, err := exec.LookPath("gcc"); err != nil {
558 t.Skip("skipping test: gcc is missing")
559 }
560 tmp := t.TempDir()
561
562 oldRegs := runtime.SetIntArgRegs(abi.IntArgRegs)
563 defer runtime.SetIntArgRegs(oldRegs)
564
565 for _, dll := range cbDLLs {
566 t.Run(dll.name, func(t *testing.T) {
567 dllPath := dll.build(t, tmp)
568 dll := syscall.MustLoadDLL(dllPath)
569 defer dll.Release()
570 for _, cbf := range getCallbackTestFuncs() {
571 t.Run(cbf.cName(false), func(t *testing.T) {
572 stdcall := syscall.NewCallback(cbf.goFunc)
573 cbf.testOne(t, dll, false, stdcall)
574 })
575 t.Run(cbf.cName(true), func(t *testing.T) {
576 cdecl := syscall.NewCallbackCDecl(cbf.goFunc)
577 cbf.testOne(t, dll, true, cdecl)
578 })
579 }
580 })
581 }
582 }
583
584 func TestRegisterClass(t *testing.T) {
585 kernel32 := GetDLL(t, "kernel32.dll")
586 user32 := GetDLL(t, "user32.dll")
587 mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0)
588 cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
589 t.Fatal("callback should never get called")
590 return 0
591 })
592 type Wndclassex struct {
593 Size uint32
594 Style uint32
595 WndProc uintptr
596 ClsExtra int32
597 WndExtra int32
598 Instance syscall.Handle
599 Icon syscall.Handle
600 Cursor syscall.Handle
601 Background syscall.Handle
602 MenuName *uint16
603 ClassName *uint16
604 IconSm syscall.Handle
605 }
606 name := syscall.StringToUTF16Ptr("test_window")
607 wc := Wndclassex{
608 WndProc: cb,
609 Instance: syscall.Handle(mh),
610 ClassName: name,
611 }
612 wc.Size = uint32(unsafe.Sizeof(wc))
613 a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc)))
614 if a == 0 {
615 t.Fatalf("RegisterClassEx failed: %v", err)
616 }
617 r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0)
618 if r == 0 {
619 t.Fatalf("UnregisterClass failed: %v", err)
620 }
621 }
622
623 func TestOutputDebugString(t *testing.T) {
624 d := GetDLL(t, "kernel32.dll")
625 p := syscall.StringToUTF16Ptr("testing OutputDebugString")
626 d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p)))
627 }
628
629 func TestRaiseException(t *testing.T) {
630 if strings.HasPrefix(testenv.Builder(), "windows-amd64-2012") {
631 testenv.SkipFlaky(t, 49681)
632 }
633 o := runTestProg(t, "testprog", "RaiseException")
634 if strings.Contains(o, "RaiseException should not return") {
635 t.Fatalf("RaiseException did not crash program: %v", o)
636 }
637 if !strings.Contains(o, "Exception 0xbad") {
638 t.Fatalf("No stack trace: %v", o)
639 }
640 }
641
642 func TestZeroDivisionException(t *testing.T) {
643 o := runTestProg(t, "testprog", "ZeroDivisionException")
644 if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
645 t.Fatalf("No stack trace: %v", o)
646 }
647 }
648
649 func TestWERDialogue(t *testing.T) {
650 if os.Getenv("TEST_WER_DIALOGUE") == "1" {
651 const EXCEPTION_NONCONTINUABLE = 1
652 mod := syscall.MustLoadDLL("kernel32.dll")
653 proc := mod.MustFindProc("RaiseException")
654 proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
655 t.Fatal("RaiseException should not return")
656 }
657 exe, err := os.Executable()
658 if err != nil {
659 t.Fatal(err)
660 }
661 cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, "-test.run=^TestWERDialogue$"))
662 cmd.Env = append(cmd.Env, "TEST_WER_DIALOGUE=1", "GOTRACEBACK=wer")
663
664
665 _, err = cmd.CombinedOutput()
666 if err == nil {
667 t.Error("test program succeeded unexpectedly")
668 }
669 }
670
671 func TestWindowsStackMemory(t *testing.T) {
672 if race.Enabled {
673 t.Skip("skipping test: race mode uses more stack memory")
674 }
675 o := runTestProg(t, "testprog", "StackMemory")
676 stackUsage, err := strconv.Atoi(o)
677 if err != nil {
678 t.Fatalf("Failed to read stack usage: %v", err)
679 }
680 if expected, got := 128<<10, stackUsage; got > expected {
681 t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got)
682 }
683 }
684
685 var used byte
686
687 func use(buf []byte) {
688 for _, c := range buf {
689 used += c
690 }
691 }
692
693 func forceStackCopy() (r int) {
694 var f func(int) int
695 f = func(i int) int {
696 var buf [256]byte
697 use(buf[:])
698 if i == 0 {
699 return 0
700 }
701 return i + f(i-1)
702 }
703 r = f(128)
704 return
705 }
706
707 func TestReturnAfterStackGrowInCallback(t *testing.T) {
708 if _, err := exec.LookPath("gcc"); err != nil {
709 t.Skip("skipping test: gcc is missing")
710 }
711
712 const src = `
713 #include <stdint.h>
714 #include <windows.h>
715
716 typedef uintptr_t __stdcall (*callback)(uintptr_t);
717
718 uintptr_t cfunc(callback f, uintptr_t n) {
719 uintptr_t r;
720 r = f(n);
721 SetLastError(333);
722 return r;
723 }
724 `
725 tmpdir := t.TempDir()
726
727 srcname := "mydll.c"
728 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
729 if err != nil {
730 t.Fatal(err)
731 }
732 outname := "mydll.dll"
733 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
734 cmd.Dir = tmpdir
735 out, err := cmd.CombinedOutput()
736 if err != nil {
737 t.Fatalf("failed to build dll: %v - %v", err, string(out))
738 }
739 dllpath := filepath.Join(tmpdir, outname)
740
741 dll := syscall.MustLoadDLL(dllpath)
742 defer dll.Release()
743
744 proc := dll.MustFindProc("cfunc")
745
746 cb := syscall.NewCallback(func(n uintptr) uintptr {
747 forceStackCopy()
748 return n
749 })
750
751
752 type result struct {
753 r uintptr
754 err syscall.Errno
755 }
756 want := result{
757
758 r: (^uintptr(0)) >> 24,
759 err: 333,
760 }
761 c := make(chan result)
762 go func() {
763 r, _, err := proc.Call(cb, want.r)
764 c <- result{r, err.(syscall.Errno)}
765 }()
766 if got := <-c; got != want {
767 t.Errorf("got %d want %d", got, want)
768 }
769 }
770
771 func TestSyscallN(t *testing.T) {
772 if _, err := exec.LookPath("gcc"); err != nil {
773 t.Skip("skipping test: gcc is missing")
774 }
775 if runtime.GOARCH != "amd64" {
776 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
777 }
778
779 for arglen := 0; arglen <= runtime.MaxArgs; arglen++ {
780 arglen := arglen
781 t.Run(fmt.Sprintf("arg-%d", arglen), func(t *testing.T) {
782 t.Parallel()
783 args := make([]string, arglen)
784 rets := make([]string, arglen+1)
785 params := make([]uintptr, arglen)
786 for i := range args {
787 args[i] = fmt.Sprintf("int a%d", i)
788 rets[i] = fmt.Sprintf("(a%d == %d)", i, i)
789 params[i] = uintptr(i)
790 }
791 rets[arglen] = "1"
792
793 src := fmt.Sprintf(`
794 #include <stdint.h>
795 #include <windows.h>
796 int cfunc(%s) { return %s; }`, strings.Join(args, ", "), strings.Join(rets, " && "))
797
798 tmpdir := t.TempDir()
799
800 srcname := "mydll.c"
801 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
802 if err != nil {
803 t.Fatal(err)
804 }
805 outname := "mydll.dll"
806 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
807 cmd.Dir = tmpdir
808 out, err := cmd.CombinedOutput()
809 if err != nil {
810 t.Fatalf("failed to build dll: %v\n%s", err, out)
811 }
812 dllpath := filepath.Join(tmpdir, outname)
813
814 dll := syscall.MustLoadDLL(dllpath)
815 defer dll.Release()
816
817 proc := dll.MustFindProc("cfunc")
818
819
820 r, _, err := proc.Call(params...)
821 if r != 1 {
822 t.Errorf("got %d want 1 (err=%v)", r, err)
823 }
824 })
825 }
826 }
827
828 func TestFloatArgs(t *testing.T) {
829 if _, err := exec.LookPath("gcc"); err != nil {
830 t.Skip("skipping test: gcc is missing")
831 }
832 if runtime.GOARCH != "amd64" {
833 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
834 }
835
836 const src = `
837 #include <stdint.h>
838 #include <windows.h>
839
840 uintptr_t cfunc(uintptr_t a, double b, float c, double d) {
841 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
842 return 1;
843 }
844 return 0;
845 }
846 `
847 tmpdir := t.TempDir()
848
849 srcname := "mydll.c"
850 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
851 if err != nil {
852 t.Fatal(err)
853 }
854 outname := "mydll.dll"
855 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
856 cmd.Dir = tmpdir
857 out, err := cmd.CombinedOutput()
858 if err != nil {
859 t.Fatalf("failed to build dll: %v - %v", err, string(out))
860 }
861 dllpath := filepath.Join(tmpdir, outname)
862
863 dll := syscall.MustLoadDLL(dllpath)
864 defer dll.Release()
865
866 proc := dll.MustFindProc("cfunc")
867
868 r, _, err := proc.Call(
869 1,
870 uintptr(math.Float64bits(2.2)),
871 uintptr(math.Float32bits(3.3)),
872 uintptr(math.Float64bits(4.4e44)),
873 )
874 if r != 1 {
875 t.Errorf("got %d want 1 (err=%v)", r, err)
876 }
877 }
878
879 func TestFloatReturn(t *testing.T) {
880 if _, err := exec.LookPath("gcc"); err != nil {
881 t.Skip("skipping test: gcc is missing")
882 }
883 if runtime.GOARCH != "amd64" {
884 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH)
885 }
886
887 const src = `
888 #include <stdint.h>
889 #include <windows.h>
890
891 float cfuncFloat(uintptr_t a, double b, float c, double d) {
892 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
893 return 1.5f;
894 }
895 return 0;
896 }
897
898 double cfuncDouble(uintptr_t a, double b, float c, double d) {
899 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) {
900 return 2.5;
901 }
902 return 0;
903 }
904 `
905 tmpdir := t.TempDir()
906
907 srcname := "mydll.c"
908 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
909 if err != nil {
910 t.Fatal(err)
911 }
912 outname := "mydll.dll"
913 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
914 cmd.Dir = tmpdir
915 out, err := cmd.CombinedOutput()
916 if err != nil {
917 t.Fatalf("failed to build dll: %v - %v", err, string(out))
918 }
919 dllpath := filepath.Join(tmpdir, outname)
920
921 dll := syscall.MustLoadDLL(dllpath)
922 defer dll.Release()
923
924 proc := dll.MustFindProc("cfuncFloat")
925
926 _, r, err := proc.Call(
927 1,
928 uintptr(math.Float64bits(2.2)),
929 uintptr(math.Float32bits(3.3)),
930 uintptr(math.Float64bits(4.4e44)),
931 )
932 fr := math.Float32frombits(uint32(r))
933 if fr != 1.5 {
934 t.Errorf("got %f want 1.5 (err=%v)", fr, err)
935 }
936
937 proc = dll.MustFindProc("cfuncDouble")
938
939 _, r, err = proc.Call(
940 1,
941 uintptr(math.Float64bits(2.2)),
942 uintptr(math.Float32bits(3.3)),
943 uintptr(math.Float64bits(4.4e44)),
944 )
945 dr := math.Float64frombits(uint64(r))
946 if dr != 2.5 {
947 t.Errorf("got %f want 2.5 (err=%v)", dr, err)
948 }
949 }
950
951 func TestTimeBeginPeriod(t *testing.T) {
952 const TIMERR_NOERROR = 0
953 if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR {
954 t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue)
955 }
956 }
957
958
959
960 func removeOneCPU(mask uintptr) (uintptr, error) {
961 if mask == 0 {
962 return 0, fmt.Errorf("cpu affinity mask is empty")
963 }
964 maskbits := int(unsafe.Sizeof(mask) * 8)
965 for i := 0; i < maskbits; i++ {
966 newmask := mask & ^(1 << uint(i))
967 if newmask != mask {
968 return newmask, nil
969 }
970
971 }
972 panic("not reached")
973 }
974
975 func resumeChildThread(kernel32 *syscall.DLL, childpid int) error {
976 _OpenThread := kernel32.MustFindProc("OpenThread")
977 _ResumeThread := kernel32.MustFindProc("ResumeThread")
978 _Thread32First := kernel32.MustFindProc("Thread32First")
979 _Thread32Next := kernel32.MustFindProc("Thread32Next")
980
981 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0)
982 if err != nil {
983 return err
984 }
985 defer syscall.CloseHandle(snapshot)
986
987 const _THREAD_SUSPEND_RESUME = 0x0002
988
989 type ThreadEntry32 struct {
990 Size uint32
991 tUsage uint32
992 ThreadID uint32
993 OwnerProcessID uint32
994 BasePri int32
995 DeltaPri int32
996 Flags uint32
997 }
998
999 var te ThreadEntry32
1000 te.Size = uint32(unsafe.Sizeof(te))
1001 ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
1002 if ret == 0 {
1003 return err
1004 }
1005 for te.OwnerProcessID != uint32(childpid) {
1006 ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
1007 if ret == 0 {
1008 return err
1009 }
1010 }
1011 h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID))
1012 if h == 0 {
1013 return err
1014 }
1015 defer syscall.Close(syscall.Handle(h))
1016
1017 ret, _, err = _ResumeThread.Call(h)
1018 if ret == 0xffffffff {
1019 return err
1020 }
1021 return nil
1022 }
1023
1024 func TestNumCPU(t *testing.T) {
1025 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1026
1027 fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU())
1028 os.Exit(0)
1029 }
1030
1031 switch n := runtime.NumberOfProcessors(); {
1032 case n < 1:
1033 t.Fatalf("system cannot have %d cpu(s)", n)
1034 case n == 1:
1035 if runtime.NumCPU() != 1 {
1036 t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU())
1037 }
1038 return
1039 }
1040
1041 const (
1042 _CREATE_SUSPENDED = 0x00000004
1043 _PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
1044 )
1045
1046 kernel32 := syscall.MustLoadDLL("kernel32.dll")
1047 _GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask")
1048 _SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask")
1049
1050 cmd := exec.Command(testenv.Executable(t), "-test.run=^TestNumCPU$")
1051 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
1052 var buf strings.Builder
1053 cmd.Stdout = &buf
1054 cmd.Stderr = &buf
1055 cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED}
1056 err := cmd.Start()
1057 if err != nil {
1058 t.Fatal(err)
1059 }
1060 defer func() {
1061 err = cmd.Wait()
1062 childOutput := buf.String()
1063 if err != nil {
1064 t.Fatalf("child failed: %v: %v", err, childOutput)
1065 }
1066
1067 want := fmt.Sprintf("%d", runtime.NumCPU()-1)
1068 if childOutput != want {
1069 t.Fatalf("child output: want %q, got %q", want, childOutput)
1070 }
1071 }()
1072
1073 defer func() {
1074 err = resumeChildThread(kernel32, cmd.Process.Pid)
1075 if err != nil {
1076 t.Fatal(err)
1077 }
1078 }()
1079
1080 ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid))
1081 if err != nil {
1082 t.Fatal(err)
1083 }
1084 defer syscall.CloseHandle(ph)
1085
1086 var mask, sysmask uintptr
1087 ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
1088 if ret == 0 {
1089 t.Fatal(err)
1090 }
1091
1092 newmask, err := removeOneCPU(mask)
1093 if err != nil {
1094 t.Fatal(err)
1095 }
1096
1097 ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask)
1098 if ret == 0 {
1099 t.Fatal(err)
1100 }
1101 ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
1102 if ret == 0 {
1103 t.Fatal(err)
1104 }
1105 if newmask != mask {
1106 t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
1107 }
1108 }
1109
1110
1111 func TestDLLPreloadMitigation(t *testing.T) {
1112 if _, err := exec.LookPath("gcc"); err != nil {
1113 t.Skip("skipping test: gcc is missing")
1114 }
1115
1116 tmpdir := t.TempDir()
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 t.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 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
1168 }
1169 }
1170
1171
1172
1173
1174
1175 func TestBigStackCallbackSyscall(t *testing.T) {
1176 if _, err := exec.LookPath("gcc"); err != nil {
1177 t.Skip("skipping test: gcc is missing")
1178 }
1179
1180 srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c")
1181 if err != nil {
1182 t.Fatal("Abs failed: ", err)
1183 }
1184
1185 tmpdir := t.TempDir()
1186
1187 outname := "mydll.dll"
1188 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
1189 cmd.Dir = tmpdir
1190 out, err := cmd.CombinedOutput()
1191 if err != nil {
1192 t.Fatalf("failed to build dll: %v - %v", err, string(out))
1193 }
1194 dllpath := filepath.Join(tmpdir, outname)
1195
1196 dll := syscall.MustLoadDLL(dllpath)
1197 defer dll.Release()
1198
1199 var ok bool
1200 proc := dll.MustFindProc("bigStack")
1201 cb := syscall.NewCallback(func() uintptr {
1202
1203 forceStackCopy()
1204 ok = true
1205 return 0
1206 })
1207 proc.Call(cb)
1208 if !ok {
1209 t.Fatalf("callback not called")
1210 }
1211 }
1212
1213 func TestSyscallStackUsage(t *testing.T) {
1214
1215
1216 syscall.Syscall15(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
1217 syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
1218 }
1219
1220 var (
1221 modwinmm = syscall.NewLazyDLL("winmm.dll")
1222 modkernel32 = syscall.NewLazyDLL("kernel32.dll")
1223
1224 procCreateEvent = modkernel32.NewProc("CreateEventW")
1225 procSetEvent = modkernel32.NewProc("SetEvent")
1226 )
1227
1228 func createEvent() (syscall.Handle, error) {
1229 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
1230 if r0 == 0 {
1231 return 0, syscall.Errno(e0)
1232 }
1233 return syscall.Handle(r0), nil
1234 }
1235
1236 func setEvent(h syscall.Handle) error {
1237 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
1238 if r0 == 0 {
1239 return syscall.Errno(e0)
1240 }
1241 return nil
1242 }
1243
1244 func BenchmarkChanToSyscallPing(b *testing.B) {
1245 n := b.N
1246 ch := make(chan int)
1247 event, err := createEvent()
1248 if err != nil {
1249 b.Fatal(err)
1250 }
1251 go func() {
1252 for i := 0; i < n; i++ {
1253 syscall.WaitForSingleObject(event, syscall.INFINITE)
1254 ch <- 1
1255 }
1256 }()
1257 for i := 0; i < n; i++ {
1258 err := setEvent(event)
1259 if err != nil {
1260 b.Fatal(err)
1261 }
1262 <-ch
1263 }
1264 }
1265
1266 func BenchmarkSyscallToSyscallPing(b *testing.B) {
1267 n := b.N
1268 event1, err := createEvent()
1269 if err != nil {
1270 b.Fatal(err)
1271 }
1272 event2, err := createEvent()
1273 if err != nil {
1274 b.Fatal(err)
1275 }
1276 go func() {
1277 for i := 0; i < n; i++ {
1278 syscall.WaitForSingleObject(event1, syscall.INFINITE)
1279 if err := setEvent(event2); err != nil {
1280 b.Errorf("Set event failed: %v", err)
1281 return
1282 }
1283 }
1284 }()
1285 for i := 0; i < n; i++ {
1286 if err := setEvent(event1); err != nil {
1287 b.Fatal(err)
1288 }
1289 if b.Failed() {
1290 break
1291 }
1292 syscall.WaitForSingleObject(event2, syscall.INFINITE)
1293 }
1294 }
1295
1296 func BenchmarkChanToChanPing(b *testing.B) {
1297 n := b.N
1298 ch1 := make(chan int)
1299 ch2 := make(chan int)
1300 go func() {
1301 for i := 0; i < n; i++ {
1302 <-ch1
1303 ch2 <- 1
1304 }
1305 }()
1306 for i := 0; i < n; i++ {
1307 ch1 <- 1
1308 <-ch2
1309 }
1310 }
1311
1312 func BenchmarkOsYield(b *testing.B) {
1313 for i := 0; i < b.N; i++ {
1314 runtime.OsYield()
1315 }
1316 }
1317
1318 func BenchmarkRunningGoProgram(b *testing.B) {
1319 tmpdir := b.TempDir()
1320
1321 src := filepath.Join(tmpdir, "main.go")
1322 err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666)
1323 if err != nil {
1324 b.Fatal(err)
1325 }
1326
1327 exe := filepath.Join(tmpdir, "main.exe")
1328 cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src)
1329 cmd.Dir = tmpdir
1330 out, err := cmd.CombinedOutput()
1331 if err != nil {
1332 b.Fatalf("building main.exe failed: %v\n%s", err, out)
1333 }
1334
1335 b.ResetTimer()
1336 for i := 0; i < b.N; i++ {
1337 cmd := exec.Command(exe)
1338 out, err := cmd.CombinedOutput()
1339 if err != nil {
1340 b.Fatalf("running main.exe failed: %v\n%s", err, out)
1341 }
1342 }
1343 }
1344
1345 const benchmarkRunningGoProgram = `
1346 package main
1347
1348 import _ "os" // average Go program will use "os" package, do the same here
1349
1350 func main() {
1351 }
1352 `
1353
View as plain text