github.com/aloncn/graphics-go@v0.0.1/src/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/syscall/windows/sysdll"
    11  	"internal/testenv"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strings"
    18  	"syscall"
    19  	"testing"
    20  	"unsafe"
    21  )
    22  
    23  type DLL struct {
    24  	*syscall.DLL
    25  	t *testing.T
    26  }
    27  
    28  func GetDLL(t *testing.T, name string) *DLL {
    29  	d, e := syscall.LoadDLL(name)
    30  	if e != nil {
    31  		t.Fatal(e)
    32  	}
    33  	return &DLL{DLL: d, t: t}
    34  }
    35  
    36  func (d *DLL) Proc(name string) *syscall.Proc {
    37  	p, e := d.FindProc(name)
    38  	if e != nil {
    39  		d.t.Fatal(e)
    40  	}
    41  	return p
    42  }
    43  
    44  func TestStdCall(t *testing.T) {
    45  	type Rect struct {
    46  		left, top, right, bottom int32
    47  	}
    48  	res := Rect{}
    49  	expected := Rect{1, 1, 40, 60}
    50  	a, _, _ := GetDLL(t, "user32.dll").Proc("UnionRect").Call(
    51  		uintptr(unsafe.Pointer(&res)),
    52  		uintptr(unsafe.Pointer(&Rect{10, 1, 14, 60})),
    53  		uintptr(unsafe.Pointer(&Rect{1, 2, 40, 50})))
    54  	if a != 1 || res.left != expected.left ||
    55  		res.top != expected.top ||
    56  		res.right != expected.right ||
    57  		res.bottom != expected.bottom {
    58  		t.Error("stdcall USER32.UnionRect returns", a, "res=", res)
    59  	}
    60  }
    61  
    62  func Test64BitReturnStdCall(t *testing.T) {
    63  
    64  	const (
    65  		VER_BUILDNUMBER      = 0x0000004
    66  		VER_MAJORVERSION     = 0x0000002
    67  		VER_MINORVERSION     = 0x0000001
    68  		VER_PLATFORMID       = 0x0000008
    69  		VER_PRODUCT_TYPE     = 0x0000080
    70  		VER_SERVICEPACKMAJOR = 0x0000020
    71  		VER_SERVICEPACKMINOR = 0x0000010
    72  		VER_SUITENAME        = 0x0000040
    73  
    74  		VER_EQUAL         = 1
    75  		VER_GREATER       = 2
    76  		VER_GREATER_EQUAL = 3
    77  		VER_LESS          = 4
    78  		VER_LESS_EQUAL    = 5
    79  
    80  		ERROR_OLD_WIN_VERSION syscall.Errno = 1150
    81  	)
    82  
    83  	type OSVersionInfoEx struct {
    84  		OSVersionInfoSize uint32
    85  		MajorVersion      uint32
    86  		MinorVersion      uint32
    87  		BuildNumber       uint32
    88  		PlatformId        uint32
    89  		CSDVersion        [128]uint16
    90  		ServicePackMajor  uint16
    91  		ServicePackMinor  uint16
    92  		SuiteMask         uint16
    93  		ProductType       byte
    94  		Reserve           byte
    95  	}
    96  
    97  	d := GetDLL(t, "kernel32.dll")
    98  
    99  	var m1, m2 uintptr
   100  	VerSetConditionMask := d.Proc("VerSetConditionMask")
   101  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MAJORVERSION, VER_GREATER_EQUAL)
   102  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_MINORVERSION, VER_GREATER_EQUAL)
   103  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)
   104  	m1, m2, _ = VerSetConditionMask.Call(m1, m2, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL)
   105  
   106  	vi := OSVersionInfoEx{
   107  		MajorVersion:     5,
   108  		MinorVersion:     1,
   109  		ServicePackMajor: 2,
   110  		ServicePackMinor: 0,
   111  	}
   112  	vi.OSVersionInfoSize = uint32(unsafe.Sizeof(vi))
   113  	r, _, e2 := d.Proc("VerifyVersionInfoW").Call(
   114  		uintptr(unsafe.Pointer(&vi)),
   115  		VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR|VER_SERVICEPACKMINOR,
   116  		m1, m2)
   117  	if r == 0 && e2 != ERROR_OLD_WIN_VERSION {
   118  		t.Errorf("VerifyVersionInfo failed: %s", e2)
   119  	}
   120  }
   121  
   122  func TestCDecl(t *testing.T) {
   123  	var buf [50]byte
   124  	fmtp, _ := syscall.BytePtrFromString("%d %d %d")
   125  	a, _, _ := GetDLL(t, "user32.dll").Proc("wsprintfA").Call(
   126  		uintptr(unsafe.Pointer(&buf[0])),
   127  		uintptr(unsafe.Pointer(fmtp)),
   128  		1000, 2000, 3000)
   129  	if string(buf[:a]) != "1000 2000 3000" {
   130  		t.Error("cdecl USER32.wsprintfA returns", a, "buf=", buf[:a])
   131  	}
   132  }
   133  
   134  func TestEnumWindows(t *testing.T) {
   135  	d := GetDLL(t, "user32.dll")
   136  	isWindows := d.Proc("IsWindow")
   137  	counter := 0
   138  	cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
   139  		if lparam != 888 {
   140  			t.Error("lparam was not passed to callback")
   141  		}
   142  		b, _, _ := isWindows.Call(uintptr(hwnd))
   143  		if b == 0 {
   144  			t.Error("USER32.IsWindow returns FALSE")
   145  		}
   146  		counter++
   147  		return 1 // continue enumeration
   148  	})
   149  	a, _, _ := d.Proc("EnumWindows").Call(cb, 888)
   150  	if a == 0 {
   151  		t.Error("USER32.EnumWindows returns FALSE")
   152  	}
   153  	if counter == 0 {
   154  		t.Error("Callback has been never called or your have no windows")
   155  	}
   156  }
   157  
   158  func callback(hwnd syscall.Handle, lparam uintptr) uintptr {
   159  	(*(*func())(unsafe.Pointer(&lparam)))()
   160  	return 0 // stop enumeration
   161  }
   162  
   163  // nestedCall calls into Windows, back into Go, and finally to f.
   164  func nestedCall(t *testing.T, f func()) {
   165  	c := syscall.NewCallback(callback)
   166  	d := GetDLL(t, "user32.dll")
   167  	defer d.Release()
   168  	d.Proc("EnumWindows").Call(c, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f))))
   169  }
   170  
   171  func TestCallback(t *testing.T) {
   172  	var x = false
   173  	nestedCall(t, func() { x = true })
   174  	if !x {
   175  		t.Fatal("nestedCall did not call func")
   176  	}
   177  }
   178  
   179  func TestCallbackGC(t *testing.T) {
   180  	nestedCall(t, runtime.GC)
   181  }
   182  
   183  func TestCallbackPanicLocked(t *testing.T) {
   184  	runtime.LockOSThread()
   185  	defer runtime.UnlockOSThread()
   186  
   187  	if !runtime.LockedOSThread() {
   188  		t.Fatal("runtime.LockOSThread didn't")
   189  	}
   190  	defer func() {
   191  		s := recover()
   192  		if s == nil {
   193  			t.Fatal("did not panic")
   194  		}
   195  		if s.(string) != "callback panic" {
   196  			t.Fatal("wrong panic:", s)
   197  		}
   198  		if !runtime.LockedOSThread() {
   199  			t.Fatal("lost lock on OS thread after panic")
   200  		}
   201  	}()
   202  	nestedCall(t, func() { panic("callback panic") })
   203  	panic("nestedCall returned")
   204  }
   205  
   206  func TestCallbackPanic(t *testing.T) {
   207  	// Make sure panic during callback unwinds properly.
   208  	if runtime.LockedOSThread() {
   209  		t.Fatal("locked OS thread on entry to TestCallbackPanic")
   210  	}
   211  	defer func() {
   212  		s := recover()
   213  		if s == nil {
   214  			t.Fatal("did not panic")
   215  		}
   216  		if s.(string) != "callback panic" {
   217  			t.Fatal("wrong panic:", s)
   218  		}
   219  		if runtime.LockedOSThread() {
   220  			t.Fatal("locked OS thread on exit from TestCallbackPanic")
   221  		}
   222  	}()
   223  	nestedCall(t, func() { panic("callback panic") })
   224  	panic("nestedCall returned")
   225  }
   226  
   227  func TestCallbackPanicLoop(t *testing.T) {
   228  	// Make sure we don't blow out m->g0 stack.
   229  	for i := 0; i < 100000; i++ {
   230  		TestCallbackPanic(t)
   231  	}
   232  }
   233  
   234  func TestBlockingCallback(t *testing.T) {
   235  	c := make(chan int)
   236  	go func() {
   237  		for i := 0; i < 10; i++ {
   238  			c <- <-c
   239  		}
   240  	}()
   241  	nestedCall(t, func() {
   242  		for i := 0; i < 10; i++ {
   243  			c <- i
   244  			if j := <-c; j != i {
   245  				t.Errorf("out of sync %d != %d", j, i)
   246  			}
   247  		}
   248  	})
   249  }
   250  
   251  func TestCallbackInAnotherThread(t *testing.T) {
   252  	// TODO: test a function which calls back in another thread: QueueUserAPC() or CreateThread()
   253  }
   254  
   255  type cbDLLFunc int // int determines number of callback parameters
   256  
   257  func (f cbDLLFunc) stdcallName() string {
   258  	return fmt.Sprintf("stdcall%d", f)
   259  }
   260  
   261  func (f cbDLLFunc) cdeclName() string {
   262  	return fmt.Sprintf("cdecl%d", f)
   263  }
   264  
   265  func (f cbDLLFunc) buildOne(stdcall bool) string {
   266  	var funcname, attr string
   267  	if stdcall {
   268  		funcname = f.stdcallName()
   269  		attr = "__stdcall"
   270  	} else {
   271  		funcname = f.cdeclName()
   272  		attr = "__cdecl"
   273  	}
   274  	typename := "t" + funcname
   275  	p := make([]string, f)
   276  	for i := range p {
   277  		p[i] = "uintptr_t"
   278  	}
   279  	params := strings.Join(p, ",")
   280  	for i := range p {
   281  		p[i] = fmt.Sprintf("%d", i+1)
   282  	}
   283  	args := strings.Join(p, ",")
   284  	return fmt.Sprintf(`
   285  typedef void %s (*%s)(%s);
   286  void %s(%s f, uintptr_t n) {
   287  	uintptr_t i;
   288  	for(i=0;i<n;i++){
   289  		f(%s);
   290  	}
   291  }
   292  	`, attr, typename, params, funcname, typename, args)
   293  }
   294  
   295  func (f cbDLLFunc) build() string {
   296  	return "#include <stdint.h>\n\n" + f.buildOne(false) + f.buildOne(true)
   297  }
   298  
   299  var cbFuncs = [...]interface{}{
   300  	2: func(i1, i2 uintptr) uintptr {
   301  		if i1+i2 != 3 {
   302  			panic("bad input")
   303  		}
   304  		return 0
   305  	},
   306  	3: func(i1, i2, i3 uintptr) uintptr {
   307  		if i1+i2+i3 != 6 {
   308  			panic("bad input")
   309  		}
   310  		return 0
   311  	},
   312  	4: func(i1, i2, i3, i4 uintptr) uintptr {
   313  		if i1+i2+i3+i4 != 10 {
   314  			panic("bad input")
   315  		}
   316  		return 0
   317  	},
   318  	5: func(i1, i2, i3, i4, i5 uintptr) uintptr {
   319  		if i1+i2+i3+i4+i5 != 15 {
   320  			panic("bad input")
   321  		}
   322  		return 0
   323  	},
   324  	6: func(i1, i2, i3, i4, i5, i6 uintptr) uintptr {
   325  		if i1+i2+i3+i4+i5+i6 != 21 {
   326  			panic("bad input")
   327  		}
   328  		return 0
   329  	},
   330  	7: func(i1, i2, i3, i4, i5, i6, i7 uintptr) uintptr {
   331  		if i1+i2+i3+i4+i5+i6+i7 != 28 {
   332  			panic("bad input")
   333  		}
   334  		return 0
   335  	},
   336  	8: func(i1, i2, i3, i4, i5, i6, i7, i8 uintptr) uintptr {
   337  		if i1+i2+i3+i4+i5+i6+i7+i8 != 36 {
   338  			panic("bad input")
   339  		}
   340  		return 0
   341  	},
   342  	9: func(i1, i2, i3, i4, i5, i6, i7, i8, i9 uintptr) uintptr {
   343  		if i1+i2+i3+i4+i5+i6+i7+i8+i9 != 45 {
   344  			panic("bad input")
   345  		}
   346  		return 0
   347  	},
   348  }
   349  
   350  type cbDLL struct {
   351  	name      string
   352  	buildArgs func(out, src string) []string
   353  }
   354  
   355  func (d *cbDLL) buildSrc(t *testing.T, path string) {
   356  	f, err := os.Create(path)
   357  	if err != nil {
   358  		t.Fatalf("failed to create source file: %v", err)
   359  	}
   360  	defer f.Close()
   361  
   362  	for i := 2; i < 10; i++ {
   363  		fmt.Fprint(f, cbDLLFunc(i).build())
   364  	}
   365  }
   366  
   367  func (d *cbDLL) build(t *testing.T, dir string) string {
   368  	srcname := d.name + ".c"
   369  	d.buildSrc(t, filepath.Join(dir, srcname))
   370  	outname := d.name + ".dll"
   371  	args := d.buildArgs(outname, srcname)
   372  	cmd := exec.Command(args[0], args[1:]...)
   373  	cmd.Dir = dir
   374  	out, err := cmd.CombinedOutput()
   375  	if err != nil {
   376  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   377  	}
   378  	return filepath.Join(dir, outname)
   379  }
   380  
   381  var cbDLLs = []cbDLL{
   382  	{
   383  		"test",
   384  		func(out, src string) []string {
   385  			return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, src}
   386  		},
   387  	},
   388  	{
   389  		"testO2",
   390  		func(out, src string) []string {
   391  			return []string{"gcc", "-shared", "-s", "-Werror", "-o", out, "-O2", src}
   392  		},
   393  	},
   394  }
   395  
   396  type cbTest struct {
   397  	n     int     // number of callback parameters
   398  	param uintptr // dll function parameter
   399  }
   400  
   401  func (test *cbTest) run(t *testing.T, dllpath string) {
   402  	dll := syscall.MustLoadDLL(dllpath)
   403  	defer dll.Release()
   404  	cb := cbFuncs[test.n]
   405  	stdcall := syscall.NewCallback(cb)
   406  	f := cbDLLFunc(test.n)
   407  	test.runOne(t, dll, f.stdcallName(), stdcall)
   408  	cdecl := syscall.NewCallbackCDecl(cb)
   409  	test.runOne(t, dll, f.cdeclName(), cdecl)
   410  }
   411  
   412  func (test *cbTest) runOne(t *testing.T, dll *syscall.DLL, proc string, cb uintptr) {
   413  	defer func() {
   414  		if r := recover(); r != nil {
   415  			t.Errorf("dll call %v(..., %d) failed: %v", proc, test.param, r)
   416  		}
   417  	}()
   418  	dll.MustFindProc(proc).Call(cb, test.param)
   419  }
   420  
   421  var cbTests = []cbTest{
   422  	{2, 1},
   423  	{2, 10000},
   424  	{3, 3},
   425  	{4, 5},
   426  	{4, 6},
   427  	{5, 2},
   428  	{6, 7},
   429  	{6, 8},
   430  	{7, 6},
   431  	{8, 1},
   432  	{9, 8},
   433  	{9, 10000},
   434  	{3, 4},
   435  	{5, 3},
   436  	{7, 7},
   437  	{8, 2},
   438  	{9, 9},
   439  }
   440  
   441  func TestStdcallAndCDeclCallbacks(t *testing.T) {
   442  	if _, err := exec.LookPath("gcc"); err != nil {
   443  		t.Skip("skipping test: gcc is missing")
   444  	}
   445  	tmp, err := ioutil.TempDir("", "TestCDeclCallback")
   446  	if err != nil {
   447  		t.Fatal("TempDir failed: ", err)
   448  	}
   449  	defer os.RemoveAll(tmp)
   450  
   451  	for _, dll := range cbDLLs {
   452  		dllPath := dll.build(t, tmp)
   453  		for _, test := range cbTests {
   454  			test.run(t, dllPath)
   455  		}
   456  	}
   457  }
   458  
   459  func TestRegisterClass(t *testing.T) {
   460  	kernel32 := GetDLL(t, "kernel32.dll")
   461  	user32 := GetDLL(t, "user32.dll")
   462  	mh, _, _ := kernel32.Proc("GetModuleHandleW").Call(0)
   463  	cb := syscall.NewCallback(func(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) (rc uintptr) {
   464  		t.Fatal("callback should never get called")
   465  		return 0
   466  	})
   467  	type Wndclassex struct {
   468  		Size       uint32
   469  		Style      uint32
   470  		WndProc    uintptr
   471  		ClsExtra   int32
   472  		WndExtra   int32
   473  		Instance   syscall.Handle
   474  		Icon       syscall.Handle
   475  		Cursor     syscall.Handle
   476  		Background syscall.Handle
   477  		MenuName   *uint16
   478  		ClassName  *uint16
   479  		IconSm     syscall.Handle
   480  	}
   481  	name := syscall.StringToUTF16Ptr("test_window")
   482  	wc := Wndclassex{
   483  		WndProc:   cb,
   484  		Instance:  syscall.Handle(mh),
   485  		ClassName: name,
   486  	}
   487  	wc.Size = uint32(unsafe.Sizeof(wc))
   488  	a, _, err := user32.Proc("RegisterClassExW").Call(uintptr(unsafe.Pointer(&wc)))
   489  	if a == 0 {
   490  		t.Fatalf("RegisterClassEx failed: %v", err)
   491  	}
   492  	r, _, err := user32.Proc("UnregisterClassW").Call(uintptr(unsafe.Pointer(name)), 0)
   493  	if r == 0 {
   494  		t.Fatalf("UnregisterClass failed: %v", err)
   495  	}
   496  }
   497  
   498  func TestOutputDebugString(t *testing.T) {
   499  	d := GetDLL(t, "kernel32.dll")
   500  	p := syscall.StringToUTF16Ptr("testing OutputDebugString")
   501  	d.Proc("OutputDebugStringW").Call(uintptr(unsafe.Pointer(p)))
   502  }
   503  
   504  func TestRaiseException(t *testing.T) {
   505  	o := runTestProg(t, "testprog", "RaiseException")
   506  	if strings.Contains(o, "RaiseException should not return") {
   507  		t.Fatalf("RaiseException did not crash program: %v", o)
   508  	}
   509  	if !strings.Contains(o, "Exception 0xbad") {
   510  		t.Fatalf("No stack trace: %v", o)
   511  	}
   512  }
   513  
   514  func TestZeroDivisionException(t *testing.T) {
   515  	o := runTestProg(t, "testprog", "ZeroDivisionException")
   516  	if !strings.Contains(o, "panic: runtime error: integer divide by zero") {
   517  		t.Fatalf("No stack trace: %v", o)
   518  	}
   519  }
   520  
   521  func TestWERDialogue(t *testing.T) {
   522  	if os.Getenv("TESTING_WER_DIALOGUE") == "1" {
   523  		defer os.Exit(0)
   524  
   525  		*runtime.TestingWER = true
   526  		const EXCEPTION_NONCONTINUABLE = 1
   527  		mod := syscall.MustLoadDLL("kernel32.dll")
   528  		proc := mod.MustFindProc("RaiseException")
   529  		proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0)
   530  		println("RaiseException should not return")
   531  		return
   532  	}
   533  	cmd := exec.Command(os.Args[0], "-test.run=TestWERDialogue")
   534  	cmd.Env = []string{"TESTING_WER_DIALOGUE=1"}
   535  	// Child process should not open WER dialogue, but return immediately instead.
   536  	cmd.CombinedOutput()
   537  }
   538  
   539  var used byte
   540  
   541  func use(buf []byte) {
   542  	for _, c := range buf {
   543  		used += c
   544  	}
   545  }
   546  
   547  func forceStackCopy() (r int) {
   548  	var f func(int) int
   549  	f = func(i int) int {
   550  		var buf [256]byte
   551  		use(buf[:])
   552  		if i == 0 {
   553  			return 0
   554  		}
   555  		return i + f(i-1)
   556  	}
   557  	r = f(128)
   558  	return
   559  }
   560  
   561  func TestReturnAfterStackGrowInCallback(t *testing.T) {
   562  	if _, err := exec.LookPath("gcc"); err != nil {
   563  		t.Skip("skipping test: gcc is missing")
   564  	}
   565  
   566  	const src = `
   567  #include <stdint.h>
   568  #include <windows.h>
   569  
   570  typedef uintptr_t __stdcall (*callback)(uintptr_t);
   571  
   572  uintptr_t cfunc(callback f, uintptr_t n) {
   573     uintptr_t r;
   574     r = f(n);
   575     SetLastError(333);
   576     return r;
   577  }
   578  `
   579  	tmpdir, err := ioutil.TempDir("", "TestReturnAfterStackGrowInCallback")
   580  	if err != nil {
   581  		t.Fatal("TempDir failed: ", err)
   582  	}
   583  	defer os.RemoveAll(tmpdir)
   584  
   585  	srcname := "mydll.c"
   586  	err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   587  	if err != nil {
   588  		t.Fatal(err)
   589  	}
   590  	outname := "mydll.dll"
   591  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname)
   592  	cmd.Dir = tmpdir
   593  	out, err := cmd.CombinedOutput()
   594  	if err != nil {
   595  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   596  	}
   597  	dllpath := filepath.Join(tmpdir, outname)
   598  
   599  	dll := syscall.MustLoadDLL(dllpath)
   600  	defer dll.Release()
   601  
   602  	proc := dll.MustFindProc("cfunc")
   603  
   604  	cb := syscall.NewCallback(func(n uintptr) uintptr {
   605  		forceStackCopy()
   606  		return n
   607  	})
   608  
   609  	// Use a new goroutine so that we get a small stack.
   610  	type result struct {
   611  		r   uintptr
   612  		err syscall.Errno
   613  	}
   614  	c := make(chan result)
   615  	go func() {
   616  		r, _, err := proc.Call(cb, 100)
   617  		c <- result{r, err.(syscall.Errno)}
   618  	}()
   619  	want := result{r: 100, err: 333}
   620  	if got := <-c; got != want {
   621  		t.Errorf("got %d want %d", got, want)
   622  	}
   623  }
   624  
   625  // removeOneCPU removes one (any) cpu from affinity mask.
   626  // It returns new affinity mask.
   627  func removeOneCPU(mask uintptr) (uintptr, error) {
   628  	if mask == 0 {
   629  		return 0, fmt.Errorf("cpu affinity mask is empty")
   630  	}
   631  	maskbits := int(unsafe.Sizeof(mask) * 8)
   632  	for i := 0; i < maskbits; i++ {
   633  		newmask := mask & ^(1 << uint(i))
   634  		if newmask != mask {
   635  			return newmask, nil
   636  		}
   637  
   638  	}
   639  	panic("not reached")
   640  }
   641  
   642  func resumeChildThread(kernel32 *syscall.DLL, childpid int) error {
   643  	_OpenThread := kernel32.MustFindProc("OpenThread")
   644  	_ResumeThread := kernel32.MustFindProc("ResumeThread")
   645  	_Thread32First := kernel32.MustFindProc("Thread32First")
   646  	_Thread32Next := kernel32.MustFindProc("Thread32Next")
   647  
   648  	snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0)
   649  	if err != nil {
   650  		return err
   651  	}
   652  	defer syscall.CloseHandle(snapshot)
   653  
   654  	const _THREAD_SUSPEND_RESUME = 0x0002
   655  
   656  	type ThreadEntry32 struct {
   657  		Size           uint32
   658  		tUsage         uint32
   659  		ThreadID       uint32
   660  		OwnerProcessID uint32
   661  		BasePri        int32
   662  		DeltaPri       int32
   663  		Flags          uint32
   664  	}
   665  
   666  	var te ThreadEntry32
   667  	te.Size = uint32(unsafe.Sizeof(te))
   668  	ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
   669  	if ret == 0 {
   670  		return err
   671  	}
   672  	for te.OwnerProcessID != uint32(childpid) {
   673  		ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te)))
   674  		if ret == 0 {
   675  			return err
   676  		}
   677  	}
   678  	h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID))
   679  	if h == 0 {
   680  		return err
   681  	}
   682  	defer syscall.Close(syscall.Handle(h))
   683  
   684  	ret, _, err = _ResumeThread.Call(h)
   685  	if ret == 0xffffffff {
   686  		return err
   687  	}
   688  	return nil
   689  }
   690  
   691  func TestNumCPU(t *testing.T) {
   692  	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
   693  		// in child process
   694  		fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU())
   695  		os.Exit(0)
   696  	}
   697  
   698  	switch n := runtime.NumberOfProcessors(); {
   699  	case n < 1:
   700  		t.Fatalf("system cannot have %d cpu(s)", n)
   701  	case n == 1:
   702  		if runtime.NumCPU() != 1 {
   703  			t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU())
   704  		}
   705  		return
   706  	}
   707  
   708  	const (
   709  		_CREATE_SUSPENDED   = 0x00000004
   710  		_PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
   711  	)
   712  
   713  	kernel32 := syscall.MustLoadDLL("kernel32.dll")
   714  	_GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask")
   715  	_SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask")
   716  
   717  	cmd := exec.Command(os.Args[0], "-test.run=TestNumCPU")
   718  	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
   719  	var buf bytes.Buffer
   720  	cmd.Stdout = &buf
   721  	cmd.Stderr = &buf
   722  	cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED}
   723  	err := cmd.Start()
   724  	if err != nil {
   725  		t.Fatal(err)
   726  	}
   727  	defer func() {
   728  		err = cmd.Wait()
   729  		childOutput := string(buf.Bytes())
   730  		if err != nil {
   731  			t.Fatalf("child failed: %v: %v", err, childOutput)
   732  		}
   733  		// removeOneCPU should have decreased child cpu count by 1
   734  		want := fmt.Sprintf("%d", runtime.NumCPU()-1)
   735  		if childOutput != want {
   736  			t.Fatalf("child output: want %q, got %q", want, childOutput)
   737  		}
   738  	}()
   739  
   740  	defer func() {
   741  		err = resumeChildThread(kernel32, cmd.Process.Pid)
   742  		if err != nil {
   743  			t.Fatal(err)
   744  		}
   745  	}()
   746  
   747  	ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid))
   748  	if err != nil {
   749  		t.Fatal(err)
   750  	}
   751  	defer syscall.CloseHandle(ph)
   752  
   753  	var mask, sysmask uintptr
   754  	ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
   755  	if ret == 0 {
   756  		t.Fatal(err)
   757  	}
   758  
   759  	newmask, err := removeOneCPU(mask)
   760  	if err != nil {
   761  		t.Fatal(err)
   762  	}
   763  
   764  	ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask)
   765  	if ret == 0 {
   766  		t.Fatal(err)
   767  	}
   768  	ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
   769  	if ret == 0 {
   770  		t.Fatal(err)
   771  	}
   772  	if newmask != mask {
   773  		t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask)
   774  	}
   775  }
   776  
   777  // See Issue 14959
   778  func TestDLLPreloadMitigation(t *testing.T) {
   779  	if _, err := exec.LookPath("gcc"); err != nil {
   780  		t.Skip("skipping test: gcc is missing")
   781  	}
   782  
   783  	tmpdir, err := ioutil.TempDir("", "TestDLLPreloadMitigation")
   784  	if err != nil {
   785  		t.Fatal("TempDir failed: ", err)
   786  	}
   787  	defer func() {
   788  		err := os.RemoveAll(tmpdir)
   789  		if err != nil {
   790  			t.Error(err)
   791  		}
   792  	}()
   793  
   794  	dir0, err := os.Getwd()
   795  	if err != nil {
   796  		t.Fatal(err)
   797  	}
   798  	defer os.Chdir(dir0)
   799  
   800  	const src = `
   801  #include <stdint.h>
   802  #include <windows.h>
   803  
   804  uintptr_t cfunc() {
   805     SetLastError(123);
   806  }
   807  `
   808  	srcname := "nojack.c"
   809  	err = ioutil.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0)
   810  	if err != nil {
   811  		t.Fatal(err)
   812  	}
   813  	name := "nojack.dll"
   814  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
   815  	cmd.Dir = tmpdir
   816  	out, err := cmd.CombinedOutput()
   817  	if err != nil {
   818  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
   819  	}
   820  	dllpath := filepath.Join(tmpdir, name)
   821  
   822  	dll := syscall.MustLoadDLL(dllpath)
   823  	dll.MustFindProc("cfunc")
   824  	dll.Release()
   825  
   826  	// Get into the directory with the DLL we'll load by base name
   827  	// ("nojack.dll") Think of this as the user double-clicking an
   828  	// installer from their Downloads directory where a browser
   829  	// silently downloaded some malicious DLLs.
   830  	os.Chdir(tmpdir)
   831  
   832  	// First before we can load a DLL from the current directory,
   833  	// loading it only as "nojack.dll", without an absolute path.
   834  	delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly
   835  	dll, err = syscall.LoadDLL(name)
   836  	if err != nil {
   837  		t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err)
   838  	}
   839  	dll.Release()
   840  
   841  	// And now verify that if we register it as a system32-only
   842  	// DLL, the implicit loading from the current directory no
   843  	// longer works.
   844  	sysdll.IsSystemDLL[name] = true
   845  	dll, err = syscall.LoadDLL(name)
   846  	if err == nil {
   847  		dll.Release()
   848  		if wantLoadLibraryEx() {
   849  			t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err)
   850  		}
   851  		t.Skip("insecure load of DLL, but expected")
   852  	}
   853  }
   854  
   855  // wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests.
   856  func wantLoadLibraryEx() bool {
   857  	return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce"
   858  }
   859  
   860  func TestLoadLibraryEx(t *testing.T) {
   861  	use, have, flags := runtime.LoadLibraryExStatus()
   862  	if use {
   863  		return // success.
   864  	}
   865  	if wantLoadLibraryEx() {
   866  		t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)",
   867  			have, flags)
   868  	}
   869  	t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)",
   870  		have, flags)
   871  }