Source file
src/runtime/syscall_windows_test.go
Documentation: runtime
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 dir0, err := os.Getwd()
1115 if err != nil {
1116 t.Fatal(err)
1117 }
1118 defer os.Chdir(dir0)
1119
1120 const src = `
1121 #include <stdint.h>
1122 #include <windows.h>
1123
1124 uintptr_t cfunc(void) {
1125 SetLastError(123);
1126 return 0;
1127 }
1128 `
1129 srcname := "nojack.c"
1130 err = os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
1131 if err != nil {
1132 t.Fatal(err)
1133 }
1134 name := "nojack.dll"
1135 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
1136 cmd.Dir = tmpdir
1137 out, err := cmd.CombinedOutput()
1138 if err != nil {
1139 t.Fatalf("failed to build dll: %v - %v", err, string(out))
1140 }
1141 dllpath := filepath.Join(tmpdir, name)
1142
1143 dll := syscall.MustLoadDLL(dllpath)
1144 dll.MustFindProc("cfunc")
1145 dll.Release()
1146
1147
1148
1149
1150
1151 os.Chdir(tmpdir)
1152
1153
1154
1155 delete(sysdll.IsSystemDLL, name)
1156 dll, err = syscall.LoadDLL(name)
1157 if err != nil {
1158 t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
1159 }
1160 dll.Release()
1161
1162
1163
1164
1165 sysdll.IsSystemDLL[name] = true
1166 dll, err = syscall.LoadDLL(name)
1167 if err == nil {
1168 dll.Release()
1169 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
1170 }
1171 }
1172
1173
1174
1175
1176
1177 func TestBigStackCallbackSyscall(t *testing.T) {
1178 if _, err := exec.LookPath("gcc"); err != nil {
1179 t.Skip("skipping test: gcc is missing")
1180 }
1181
1182 srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c")
1183 if err != nil {
1184 t.Fatal("Abs failed: ", err)
1185 }
1186
1187 tmpdir := t.TempDir()
1188
1189 outname := "mydll.dll"
1190 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
1191 cmd.Dir = tmpdir
1192 out, err := cmd.CombinedOutput()
1193 if err != nil {
1194 t.Fatalf("failed to build dll: %v - %v", err, string(out))
1195 }
1196 dllpath := filepath.Join(tmpdir, outname)
1197
1198 dll := syscall.MustLoadDLL(dllpath)
1199 defer dll.Release()
1200
1201 var ok bool
1202 proc := dll.MustFindProc("bigStack")
1203 cb := syscall.NewCallback(func() uintptr {
1204
1205 forceStackCopy()
1206 ok = true
1207 return 0
1208 })
1209 proc.Call(cb)
1210 if !ok {
1211 t.Fatalf("callback not called")
1212 }
1213 }
1214
1215 var (
1216 modwinmm = syscall.NewLazyDLL("winmm.dll")
1217 modkernel32 = syscall.NewLazyDLL("kernel32.dll")
1218
1219 procCreateEvent = modkernel32.NewProc("CreateEventW")
1220 procSetEvent = modkernel32.NewProc("SetEvent")
1221 )
1222
1223 func createEvent() (syscall.Handle, error) {
1224 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
1225 if r0 == 0 {
1226 return 0, syscall.Errno(e0)
1227 }
1228 return syscall.Handle(r0), nil
1229 }
1230
1231 func setEvent(h syscall.Handle) error {
1232 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
1233 if r0 == 0 {
1234 return syscall.Errno(e0)
1235 }
1236 return nil
1237 }
1238
1239 func BenchmarkChanToSyscallPing(b *testing.B) {
1240 n := b.N
1241 ch := make(chan int)
1242 event, err := createEvent()
1243 if err != nil {
1244 b.Fatal(err)
1245 }
1246 go func() {
1247 for i := 0; i < n; i++ {
1248 syscall.WaitForSingleObject(event, syscall.INFINITE)
1249 ch <- 1
1250 }
1251 }()
1252 for i := 0; i < n; i++ {
1253 err := setEvent(event)
1254 if err != nil {
1255 b.Fatal(err)
1256 }
1257 <-ch
1258 }
1259 }
1260
1261 func BenchmarkSyscallToSyscallPing(b *testing.B) {
1262 n := b.N
1263 event1, err := createEvent()
1264 if err != nil {
1265 b.Fatal(err)
1266 }
1267 event2, err := createEvent()
1268 if err != nil {
1269 b.Fatal(err)
1270 }
1271 go func() {
1272 for i := 0; i < n; i++ {
1273 syscall.WaitForSingleObject(event1, syscall.INFINITE)
1274 if err := setEvent(event2); err != nil {
1275 b.Errorf("Set event failed: %v", err)
1276 return
1277 }
1278 }
1279 }()
1280 for i := 0; i < n; i++ {
1281 if err := setEvent(event1); err != nil {
1282 b.Fatal(err)
1283 }
1284 if b.Failed() {
1285 break
1286 }
1287 syscall.WaitForSingleObject(event2, syscall.INFINITE)
1288 }
1289 }
1290
1291 func BenchmarkChanToChanPing(b *testing.B) {
1292 n := b.N
1293 ch1 := make(chan int)
1294 ch2 := make(chan int)
1295 go func() {
1296 for i := 0; i < n; i++ {
1297 <-ch1
1298 ch2 <- 1
1299 }
1300 }()
1301 for i := 0; i < n; i++ {
1302 ch1 <- 1
1303 <-ch2
1304 }
1305 }
1306
1307 func BenchmarkOsYield(b *testing.B) {
1308 for i := 0; i < b.N; i++ {
1309 runtime.OsYield()
1310 }
1311 }
1312
1313 func BenchmarkRunningGoProgram(b *testing.B) {
1314 tmpdir := b.TempDir()
1315
1316 src := filepath.Join(tmpdir, "main.go")
1317 err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666)
1318 if err != nil {
1319 b.Fatal(err)
1320 }
1321
1322 exe := filepath.Join(tmpdir, "main.exe")
1323 cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src)
1324 cmd.Dir = tmpdir
1325 out, err := cmd.CombinedOutput()
1326 if err != nil {
1327 b.Fatalf("building main.exe failed: %v\n%s", err, out)
1328 }
1329
1330 b.ResetTimer()
1331 for i := 0; i < b.N; i++ {
1332 cmd := exec.Command(exe)
1333 out, err := cmd.CombinedOutput()
1334 if err != nil {
1335 b.Fatalf("running main.exe failed: %v\n%s", err, out)
1336 }
1337 }
1338 }
1339
1340 const benchmarkRunningGoProgram = `
1341 package main
1342
1343 import _ "os" // average Go program will use "os" package, do the same here
1344
1345 func main() {
1346 }
1347 `
1348
View as plain text