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