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