github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/runtime/syscall_windows_test.go (about)

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