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 func TestSyscallStackUsage(t *testing.T) {
1216
1217
1218 syscall.Syscall15(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
1219 syscall.Syscall18(procSetEvent.Addr(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
1220 }
1221
1222 var (
1223 modwinmm = syscall.NewLazyDLL("winmm.dll")
1224 modkernel32 = syscall.NewLazyDLL("kernel32.dll")
1225
1226 procCreateEvent = modkernel32.NewProc("CreateEventW")
1227 procSetEvent = modkernel32.NewProc("SetEvent")
1228 )
1229
1230 func createEvent() (syscall.Handle, error) {
1231 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0)
1232 if r0 == 0 {
1233 return 0, syscall.Errno(e0)
1234 }
1235 return syscall.Handle(r0), nil
1236 }
1237
1238 func setEvent(h syscall.Handle) error {
1239 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0)
1240 if r0 == 0 {
1241 return syscall.Errno(e0)
1242 }
1243 return nil
1244 }
1245
1246 func BenchmarkChanToSyscallPing(b *testing.B) {
1247 n := b.N
1248 ch := make(chan int)
1249 event, err := createEvent()
1250 if err != nil {
1251 b.Fatal(err)
1252 }
1253 go func() {
1254 for i := 0; i < n; i++ {
1255 syscall.WaitForSingleObject(event, syscall.INFINITE)
1256 ch <- 1
1257 }
1258 }()
1259 for i := 0; i < n; i++ {
1260 err := setEvent(event)
1261 if err != nil {
1262 b.Fatal(err)
1263 }
1264 <-ch
1265 }
1266 }
1267
1268 func BenchmarkSyscallToSyscallPing(b *testing.B) {
1269 n := b.N
1270 event1, err := createEvent()
1271 if err != nil {
1272 b.Fatal(err)
1273 }
1274 event2, err := createEvent()
1275 if err != nil {
1276 b.Fatal(err)
1277 }
1278 go func() {
1279 for i := 0; i < n; i++ {
1280 syscall.WaitForSingleObject(event1, syscall.INFINITE)
1281 if err := setEvent(event2); err != nil {
1282 b.Errorf("Set event failed: %v", err)
1283 return
1284 }
1285 }
1286 }()
1287 for i := 0; i < n; i++ {
1288 if err := setEvent(event1); err != nil {
1289 b.Fatal(err)
1290 }
1291 if b.Failed() {
1292 break
1293 }
1294 syscall.WaitForSingleObject(event2, syscall.INFINITE)
1295 }
1296 }
1297
1298 func BenchmarkChanToChanPing(b *testing.B) {
1299 n := b.N
1300 ch1 := make(chan int)
1301 ch2 := make(chan int)
1302 go func() {
1303 for i := 0; i < n; i++ {
1304 <-ch1
1305 ch2 <- 1
1306 }
1307 }()
1308 for i := 0; i < n; i++ {
1309 ch1 <- 1
1310 <-ch2
1311 }
1312 }
1313
1314 func BenchmarkOsYield(b *testing.B) {
1315 for i := 0; i < b.N; i++ {
1316 runtime.OsYield()
1317 }
1318 }
1319
1320 func BenchmarkRunningGoProgram(b *testing.B) {
1321 tmpdir := b.TempDir()
1322
1323 src := filepath.Join(tmpdir, "main.go")
1324 err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666)
1325 if err != nil {
1326 b.Fatal(err)
1327 }
1328
1329 exe := filepath.Join(tmpdir, "main.exe")
1330 cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src)
1331 cmd.Dir = tmpdir
1332 out, err := cmd.CombinedOutput()
1333 if err != nil {
1334 b.Fatalf("building main.exe failed: %v\n%s", err, out)
1335 }
1336
1337 b.ResetTimer()
1338 for i := 0; i < b.N; i++ {
1339 cmd := exec.Command(exe)
1340 out, err := cmd.CombinedOutput()
1341 if err != nil {
1342 b.Fatalf("running main.exe failed: %v\n%s", err, out)
1343 }
1344 }
1345 }
1346
1347 const benchmarkRunningGoProgram = `
1348 package main
1349
1350 import _ "os" // average Go program will use "os" package, do the same here
1351
1352 func main() {
1353 }
1354 `
1355
View as plain text