1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package runtime_test
6
7import (
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
26type DLL struct {
27	*syscall.DLL
28	t *testing.T
29}
30
31func 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
39func (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
47func 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
65func 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
125func 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
137func 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 // continue enumeration
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
161func callback(timeFormatString unsafe.Pointer, lparam uintptr) uintptr {
162	(*(*func())(unsafe.Pointer(&lparam)))()
163	return 0 // stop enumeration
164}
165
166// nestedCall calls into Windows, back into Go, and finally to f.
167func 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
175func 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
183func TestCallbackGC(t *testing.T) {
184	nestedCall(t, runtime.GC)
185}
186
187func 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
210func TestCallbackPanic(t *testing.T) {
211	// Make sure panic during callback unwinds properly.
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
231func TestCallbackPanicLoop(t *testing.T) {
232	// Make sure we don't blow out m->g0 stack.
233	for i := 0; i < 100000; i++ {
234		TestCallbackPanic(t)
235	}
236}
237
238func 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
255func 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
287type cbFunc struct {
288	goFunc any
289}
290
291func (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
303func (f cbFunc) cSrc(w io.Writer, cdecl bool) {
304	// Construct a C function that takes a callback with
305	// f.goFunc's signature, and calls it with integers 1..N.
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		// We included stdint.h, so this works for all sized
317		// integer types, and uint8Pair_t.
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, `
326typedef uintptr_t %s (*%s)(%s);
327uintptr_t %s(%s f) {
328	return f(%s);
329}
330	`, attr, typename, strings.Join(cTypes, ","), funcname, typename, strings.Join(cArgs, ","))
331}
332
333func (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
345type uint8Pair struct{ x, y uint8 }
346
347var 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	// Non-uintptr parameters.
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//go:registerparams
396func sum2(i1, i2 uintptr) uintptr {
397	return i1 + i2
398}
399
400//go:registerparams
401func sum3(i1, i2, i3 uintptr) uintptr {
402	return i1 + i2 + i3
403}
404
405//go:registerparams
406func sum4(i1, i2, i3, i4 uintptr) uintptr {
407	return i1 + i2 + i3 + i4
408}
409
410//go:registerparams
411func sum5(i1, i2, i3, i4, i5 uintptr) uintptr {
412	return i1 + i2 + i3 + i4 + i5
413}
414
415//go:registerparams
416func sum6(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
417	return i1 + i2 + i3 + i4 + i5 + i6
418}
419
420//go:registerparams
421func sum7(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
422	return i1 + i2 + i3 + i4 + i5 + i6 + i7
423}
424
425//go:registerparams
426func sum8(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
427	return i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8
428}
429
430//go:registerparams
431func 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//go:registerparams
436func 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//go:registerparams
441func 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//go:registerparams
446func 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//go:registerparams
451func 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//go:registerparams
456func sum5mix(i1 int8, i2 int16, i3 int32, i4, i5 uintptr) uintptr {
457	return uintptr(i1) + uintptr(i2) + uintptr(i3) + i4 + i5
458}
459
460//go:registerparams
461func 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// This test forces a GC. The idea is to have enough arguments
466// that insufficient spill slots allocated (according to the ABI)
467// may cause compiler-generated spills to clobber the return PC.
468// Then, the GC stack scanning will catch that.
469//
470//go:registerparams
471func 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// TODO(register args): Remove this once we switch to using the register
477// calling convention by default, since this is redundant with the existing
478// tests.
479var 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
497func getCallbackTestFuncs() []cbFunc {
498	if regs := runtime.SetIntArgRegs(-1); regs > 0 {
499		return cbFuncsRegABI
500	}
501	return cbFuncs
502}
503
504type cbDLL struct {
505	name      string
506	buildArgs func(out, src string) []string
507}
508
509func (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>
518typedef 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
526func (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
540var 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
555func 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
583func 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
622func 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
628func 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
641func 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
648func 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	// Child process should not open WER dialogue, but return immediately instead.
663	// The exit code can't be reliably tested here because Windows can change it.
664	_, err = cmd.CombinedOutput()
665	if err == nil {
666		t.Error("test program succeeded unexpectedly")
667	}
668}
669
670func 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
681var used byte
682
683func use(buf []byte) {
684	for _, c := range buf {
685		used += c
686	}
687}
688
689func 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
703func 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
712typedef uintptr_t __stdcall (*callback)(uintptr_t);
713
714uintptr_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	// Use a new goroutine so that we get a small stack.
748	type result struct {
749		r   uintptr
750		err syscall.Errno
751	}
752	want := result{
753		// Make it large enough to test issue #29331.
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
767func 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" // for arglen == 0
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			// proc.Call() will call SyscallN() internally.
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
824func 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
836uintptr_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
875func 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
887float 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
894double 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
947func 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// removeOneCPU removes one (any) cpu from affinity mask.
955// It returns new affinity mask.
956func 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
971func 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
1020func TestNumCPU(t *testing.T) {
1021	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
1022		// in child process
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		// removeOneCPU should have decreased child cpu count by 1
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// See Issue 14959
1107func 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
1124uintptr_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	// Get into the directory with the DLL we'll load by base name
1148	// ("nojack.dll") Think of this as the user double-clicking an
1149	// installer from their Downloads directory where a browser
1150	// silently downloaded some malicious DLLs.
1151	os.Chdir(tmpdir)
1152
1153	// First before we can load a DLL from the current directory,
1154	// loading it only as "nojack.dll", without an absolute path.
1155	delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly
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	// And now verify that if we register it as a system32-only
1163	// DLL, the implicit loading from the current directory no
1164	// longer works.
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// Test that C code called via a DLL can use large Windows thread
1174// stacks and call back in to Go without crashing. See issue #20975.
1175//
1176// See also TestBigStackCallbackCgo.
1177func 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		// Do something interesting to force stack checks.
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
1215var (
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
1223func 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
1231func 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
1239func 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
1261func 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
1291func 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
1307func BenchmarkOsYield(b *testing.B) {
1308	for i := 0; i < b.N; i++ {
1309		runtime.OsYield()
1310	}
1311}
1312
1313func 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
1340const benchmarkRunningGoProgram = `
1341package main
1342
1343import _ "os" // average Go program will use "os" package, do the same here
1344
1345func main() {
1346}
1347`
1348