github.com/comwrg/go/src@v0.0.0-20220319063731-c238d0440370/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 "github.com/comwrg/go/src/internal/abi" 11 "github.com/comwrg/go/src/internal/syscall/windows/sysdll" 12 "github.com/comwrg/go/src/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 TestSyscall18(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 const src = ` 771 #include <stdint.h> 772 #include <windows.h> 773 774 int cfunc( int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, 775 int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18) { 776 return 1; 777 } 778 ` 779 tmpdir := t.TempDir() 780 781 srcname := "mydll.c" 782 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 783 if err != nil { 784 t.Fatal(err) 785 } 786 outname := "mydll.dll" 787 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 788 cmd.Dir = tmpdir 789 out, err := cmd.CombinedOutput() 790 if err != nil { 791 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 792 } 793 dllpath := filepath.Join(tmpdir, outname) 794 795 dll := syscall.MustLoadDLL(dllpath) 796 defer dll.Release() 797 798 proc := dll.MustFindProc("cfunc") 799 800 // proc.Call() will call Syscall18() internally. 801 r, _, err := proc.Call(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18) 802 if r != 1 { 803 t.Errorf("got %d want 1 (err=%v)", r, err) 804 } 805 } 806 807 func TestFloatArgs(t *testing.T) { 808 if _, err := exec.LookPath("gcc"); err != nil { 809 t.Skip("skipping test: gcc is missing") 810 } 811 if runtime.GOARCH != "amd64" { 812 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH) 813 } 814 815 const src = ` 816 #include <stdint.h> 817 #include <windows.h> 818 819 uintptr_t cfunc(uintptr_t a, double b, float c, double d) { 820 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 821 return 1; 822 } 823 return 0; 824 } 825 ` 826 tmpdir := t.TempDir() 827 828 srcname := "mydll.c" 829 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 830 if err != nil { 831 t.Fatal(err) 832 } 833 outname := "mydll.dll" 834 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 835 cmd.Dir = tmpdir 836 out, err := cmd.CombinedOutput() 837 if err != nil { 838 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 839 } 840 dllpath := filepath.Join(tmpdir, outname) 841 842 dll := syscall.MustLoadDLL(dllpath) 843 defer dll.Release() 844 845 proc := dll.MustFindProc("cfunc") 846 847 r, _, err := proc.Call( 848 1, 849 uintptr(math.Float64bits(2.2)), 850 uintptr(math.Float32bits(3.3)), 851 uintptr(math.Float64bits(4.4e44)), 852 ) 853 if r != 1 { 854 t.Errorf("got %d want 1 (err=%v)", r, err) 855 } 856 } 857 858 func TestFloatReturn(t *testing.T) { 859 if _, err := exec.LookPath("gcc"); err != nil { 860 t.Skip("skipping test: gcc is missing") 861 } 862 if runtime.GOARCH != "amd64" { 863 t.Skipf("skipping test: GOARCH=%s", runtime.GOARCH) 864 } 865 866 const src = ` 867 #include <stdint.h> 868 #include <windows.h> 869 870 float cfuncFloat(uintptr_t a, double b, float c, double d) { 871 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 872 return 1.5f; 873 } 874 return 0; 875 } 876 877 double cfuncDouble(uintptr_t a, double b, float c, double d) { 878 if (a == 1 && b == 2.2 && c == 3.3f && d == 4.4e44) { 879 return 2.5; 880 } 881 return 0; 882 } 883 ` 884 tmpdir := t.TempDir() 885 886 srcname := "mydll.c" 887 err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 888 if err != nil { 889 t.Fatal(err) 890 } 891 outname := "mydll.dll" 892 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 893 cmd.Dir = tmpdir 894 out, err := cmd.CombinedOutput() 895 if err != nil { 896 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 897 } 898 dllpath := filepath.Join(tmpdir, outname) 899 900 dll := syscall.MustLoadDLL(dllpath) 901 defer dll.Release() 902 903 proc := dll.MustFindProc("cfuncFloat") 904 905 _, r, err := proc.Call( 906 1, 907 uintptr(math.Float64bits(2.2)), 908 uintptr(math.Float32bits(3.3)), 909 uintptr(math.Float64bits(4.4e44)), 910 ) 911 fr := math.Float32frombits(uint32(r)) 912 if fr != 1.5 { 913 t.Errorf("got %f want 1.5 (err=%v)", fr, err) 914 } 915 916 proc = dll.MustFindProc("cfuncDouble") 917 918 _, r, err = proc.Call( 919 1, 920 uintptr(math.Float64bits(2.2)), 921 uintptr(math.Float32bits(3.3)), 922 uintptr(math.Float64bits(4.4e44)), 923 ) 924 dr := math.Float64frombits(uint64(r)) 925 if dr != 2.5 { 926 t.Errorf("got %f want 2.5 (err=%v)", dr, err) 927 } 928 } 929 930 func TestTimeBeginPeriod(t *testing.T) { 931 const TIMERR_NOERROR = 0 932 if *runtime.TimeBeginPeriodRetValue != TIMERR_NOERROR { 933 t.Fatalf("timeBeginPeriod failed: it returned %d", *runtime.TimeBeginPeriodRetValue) 934 } 935 } 936 937 // removeOneCPU removes one (any) cpu from affinity mask. 938 // It returns new affinity mask. 939 func removeOneCPU(mask uintptr) (uintptr, error) { 940 if mask == 0 { 941 return 0, fmt.Errorf("cpu affinity mask is empty") 942 } 943 maskbits := int(unsafe.Sizeof(mask) * 8) 944 for i := 0; i < maskbits; i++ { 945 newmask := mask & ^(1 << uint(i)) 946 if newmask != mask { 947 return newmask, nil 948 } 949 950 } 951 panic("not reached") 952 } 953 954 func resumeChildThread(kernel32 *syscall.DLL, childpid int) error { 955 _OpenThread := kernel32.MustFindProc("OpenThread") 956 _ResumeThread := kernel32.MustFindProc("ResumeThread") 957 _Thread32First := kernel32.MustFindProc("Thread32First") 958 _Thread32Next := kernel32.MustFindProc("Thread32Next") 959 960 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPTHREAD, 0) 961 if err != nil { 962 return err 963 } 964 defer syscall.CloseHandle(snapshot) 965 966 const _THREAD_SUSPEND_RESUME = 0x0002 967 968 type ThreadEntry32 struct { 969 Size uint32 970 tUsage uint32 971 ThreadID uint32 972 OwnerProcessID uint32 973 BasePri int32 974 DeltaPri int32 975 Flags uint32 976 } 977 978 var te ThreadEntry32 979 te.Size = uint32(unsafe.Sizeof(te)) 980 ret, _, err := _Thread32First.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 981 if ret == 0 { 982 return err 983 } 984 for te.OwnerProcessID != uint32(childpid) { 985 ret, _, err = _Thread32Next.Call(uintptr(snapshot), uintptr(unsafe.Pointer(&te))) 986 if ret == 0 { 987 return err 988 } 989 } 990 h, _, err := _OpenThread.Call(_THREAD_SUSPEND_RESUME, 1, uintptr(te.ThreadID)) 991 if h == 0 { 992 return err 993 } 994 defer syscall.Close(syscall.Handle(h)) 995 996 ret, _, err = _ResumeThread.Call(h) 997 if ret == 0xffffffff { 998 return err 999 } 1000 return nil 1001 } 1002 1003 func TestNumCPU(t *testing.T) { 1004 if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { 1005 // in child process 1006 fmt.Fprintf(os.Stderr, "%d", runtime.NumCPU()) 1007 os.Exit(0) 1008 } 1009 1010 switch n := runtime.NumberOfProcessors(); { 1011 case n < 1: 1012 t.Fatalf("system cannot have %d cpu(s)", n) 1013 case n == 1: 1014 if runtime.NumCPU() != 1 { 1015 t.Fatalf("runtime.NumCPU() returns %d on single cpu system", runtime.NumCPU()) 1016 } 1017 return 1018 } 1019 1020 const ( 1021 _CREATE_SUSPENDED = 0x00000004 1022 _PROCESS_ALL_ACCESS = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff 1023 ) 1024 1025 kernel32 := syscall.MustLoadDLL("kernel32.dll") 1026 _GetProcessAffinityMask := kernel32.MustFindProc("GetProcessAffinityMask") 1027 _SetProcessAffinityMask := kernel32.MustFindProc("SetProcessAffinityMask") 1028 1029 cmd := exec.Command(os.Args[0], "-test.run=TestNumCPU") 1030 cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") 1031 var buf bytes.Buffer 1032 cmd.Stdout = &buf 1033 cmd.Stderr = &buf 1034 cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: _CREATE_SUSPENDED} 1035 err := cmd.Start() 1036 if err != nil { 1037 t.Fatal(err) 1038 } 1039 defer func() { 1040 err = cmd.Wait() 1041 childOutput := string(buf.Bytes()) 1042 if err != nil { 1043 t.Fatalf("child failed: %v: %v", err, childOutput) 1044 } 1045 // removeOneCPU should have decreased child cpu count by 1 1046 want := fmt.Sprintf("%d", runtime.NumCPU()-1) 1047 if childOutput != want { 1048 t.Fatalf("child output: want %q, got %q", want, childOutput) 1049 } 1050 }() 1051 1052 defer func() { 1053 err = resumeChildThread(kernel32, cmd.Process.Pid) 1054 if err != nil { 1055 t.Fatal(err) 1056 } 1057 }() 1058 1059 ph, err := syscall.OpenProcess(_PROCESS_ALL_ACCESS, false, uint32(cmd.Process.Pid)) 1060 if err != nil { 1061 t.Fatal(err) 1062 } 1063 defer syscall.CloseHandle(ph) 1064 1065 var mask, sysmask uintptr 1066 ret, _, err := _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 1067 if ret == 0 { 1068 t.Fatal(err) 1069 } 1070 1071 newmask, err := removeOneCPU(mask) 1072 if err != nil { 1073 t.Fatal(err) 1074 } 1075 1076 ret, _, err = _SetProcessAffinityMask.Call(uintptr(ph), newmask) 1077 if ret == 0 { 1078 t.Fatal(err) 1079 } 1080 ret, _, err = _GetProcessAffinityMask.Call(uintptr(ph), uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) 1081 if ret == 0 { 1082 t.Fatal(err) 1083 } 1084 if newmask != mask { 1085 t.Fatalf("SetProcessAffinityMask didn't set newmask of 0x%x. Current mask is 0x%x.", newmask, mask) 1086 } 1087 } 1088 1089 // See Issue 14959 1090 func TestDLLPreloadMitigation(t *testing.T) { 1091 if _, err := exec.LookPath("gcc"); err != nil { 1092 t.Skip("skipping test: gcc is missing") 1093 } 1094 1095 tmpdir := t.TempDir() 1096 1097 dir0, err := os.Getwd() 1098 if err != nil { 1099 t.Fatal(err) 1100 } 1101 defer os.Chdir(dir0) 1102 1103 const src = ` 1104 #include <stdint.h> 1105 #include <windows.h> 1106 1107 uintptr_t cfunc(void) { 1108 SetLastError(123); 1109 return 0; 1110 } 1111 ` 1112 srcname := "nojack.c" 1113 err = os.WriteFile(filepath.Join(tmpdir, srcname), []byte(src), 0) 1114 if err != nil { 1115 t.Fatal(err) 1116 } 1117 name := "nojack.dll" 1118 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname) 1119 cmd.Dir = tmpdir 1120 out, err := cmd.CombinedOutput() 1121 if err != nil { 1122 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 1123 } 1124 dllpath := filepath.Join(tmpdir, name) 1125 1126 dll := syscall.MustLoadDLL(dllpath) 1127 dll.MustFindProc("cfunc") 1128 dll.Release() 1129 1130 // Get into the directory with the DLL we'll load by base name 1131 // ("nojack.dll") Think of this as the user double-clicking an 1132 // installer from their Downloads directory where a browser 1133 // silently downloaded some malicious DLLs. 1134 os.Chdir(tmpdir) 1135 1136 // First before we can load a DLL from the current directory, 1137 // loading it only as "nojack.dll", without an absolute path. 1138 delete(sysdll.IsSystemDLL, name) // in case test was run repeatedly 1139 dll, err = syscall.LoadDLL(name) 1140 if err != nil { 1141 t.Fatalf("failed to load %s by base name before sysdll registration: %v", name, err) 1142 } 1143 dll.Release() 1144 1145 // And now verify that if we register it as a system32-only 1146 // DLL, the implicit loading from the current directory no 1147 // longer works. 1148 sysdll.IsSystemDLL[name] = true 1149 dll, err = syscall.LoadDLL(name) 1150 if err == nil { 1151 dll.Release() 1152 if wantLoadLibraryEx() { 1153 t.Fatalf("Bad: insecure load of DLL by base name %q before sysdll registration: %v", name, err) 1154 } 1155 t.Skip("insecure load of DLL, but expected") 1156 } 1157 } 1158 1159 // Test that C code called via a DLL can use large Windows thread 1160 // stacks and call back in to Go without crashing. See issue #20975. 1161 // 1162 // See also TestBigStackCallbackCgo. 1163 func TestBigStackCallbackSyscall(t *testing.T) { 1164 if _, err := exec.LookPath("gcc"); err != nil { 1165 t.Skip("skipping test: gcc is missing") 1166 } 1167 1168 srcname, err := filepath.Abs("testdata/testprogcgo/bigstack_windows.c") 1169 if err != nil { 1170 t.Fatal("Abs failed: ", err) 1171 } 1172 1173 tmpdir := t.TempDir() 1174 1175 outname := "mydll.dll" 1176 cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", outname, srcname) 1177 cmd.Dir = tmpdir 1178 out, err := cmd.CombinedOutput() 1179 if err != nil { 1180 t.Fatalf("failed to build dll: %v - %v", err, string(out)) 1181 } 1182 dllpath := filepath.Join(tmpdir, outname) 1183 1184 dll := syscall.MustLoadDLL(dllpath) 1185 defer dll.Release() 1186 1187 var ok bool 1188 proc := dll.MustFindProc("bigStack") 1189 cb := syscall.NewCallback(func() uintptr { 1190 // Do something interesting to force stack checks. 1191 forceStackCopy() 1192 ok = true 1193 return 0 1194 }) 1195 proc.Call(cb) 1196 if !ok { 1197 t.Fatalf("callback not called") 1198 } 1199 } 1200 1201 // wantLoadLibraryEx reports whether we expect LoadLibraryEx to work for tests. 1202 func wantLoadLibraryEx() bool { 1203 return testenv.Builder() == "windows-amd64-gce" || testenv.Builder() == "windows-386-gce" 1204 } 1205 1206 func TestLoadLibraryEx(t *testing.T) { 1207 use, have, flags := runtime.LoadLibraryExStatus() 1208 if use { 1209 return // success. 1210 } 1211 if wantLoadLibraryEx() { 1212 t.Fatalf("Expected LoadLibraryEx+flags to be available. (LoadLibraryEx=%v; flags=%v)", 1213 have, flags) 1214 } 1215 t.Skipf("LoadLibraryEx not usable, but not expected. (LoadLibraryEx=%v; flags=%v)", 1216 have, flags) 1217 } 1218 1219 var ( 1220 modwinmm = syscall.NewLazyDLL("winmm.dll") 1221 modkernel32 = syscall.NewLazyDLL("kernel32.dll") 1222 1223 procCreateEvent = modkernel32.NewProc("CreateEventW") 1224 procSetEvent = modkernel32.NewProc("SetEvent") 1225 ) 1226 1227 func createEvent() (syscall.Handle, error) { 1228 r0, _, e0 := syscall.Syscall6(procCreateEvent.Addr(), 4, 0, 0, 0, 0, 0, 0) 1229 if r0 == 0 { 1230 return 0, syscall.Errno(e0) 1231 } 1232 return syscall.Handle(r0), nil 1233 } 1234 1235 func setEvent(h syscall.Handle) error { 1236 r0, _, e0 := syscall.Syscall(procSetEvent.Addr(), 1, uintptr(h), 0, 0) 1237 if r0 == 0 { 1238 return syscall.Errno(e0) 1239 } 1240 return nil 1241 } 1242 1243 func BenchmarkChanToSyscallPing(b *testing.B) { 1244 n := b.N 1245 ch := make(chan int) 1246 event, err := createEvent() 1247 if err != nil { 1248 b.Fatal(err) 1249 } 1250 go func() { 1251 for i := 0; i < n; i++ { 1252 syscall.WaitForSingleObject(event, syscall.INFINITE) 1253 ch <- 1 1254 } 1255 }() 1256 for i := 0; i < n; i++ { 1257 err := setEvent(event) 1258 if err != nil { 1259 b.Fatal(err) 1260 } 1261 <-ch 1262 } 1263 } 1264 1265 func BenchmarkSyscallToSyscallPing(b *testing.B) { 1266 n := b.N 1267 event1, err := createEvent() 1268 if err != nil { 1269 b.Fatal(err) 1270 } 1271 event2, err := createEvent() 1272 if err != nil { 1273 b.Fatal(err) 1274 } 1275 go func() { 1276 for i := 0; i < n; i++ { 1277 syscall.WaitForSingleObject(event1, syscall.INFINITE) 1278 if err := setEvent(event2); err != nil { 1279 b.Errorf("Set event failed: %v", err) 1280 return 1281 } 1282 } 1283 }() 1284 for i := 0; i < n; i++ { 1285 if err := setEvent(event1); err != nil { 1286 b.Fatal(err) 1287 } 1288 if b.Failed() { 1289 break 1290 } 1291 syscall.WaitForSingleObject(event2, syscall.INFINITE) 1292 } 1293 } 1294 1295 func BenchmarkChanToChanPing(b *testing.B) { 1296 n := b.N 1297 ch1 := make(chan int) 1298 ch2 := make(chan int) 1299 go func() { 1300 for i := 0; i < n; i++ { 1301 <-ch1 1302 ch2 <- 1 1303 } 1304 }() 1305 for i := 0; i < n; i++ { 1306 ch1 <- 1 1307 <-ch2 1308 } 1309 } 1310 1311 func BenchmarkOsYield(b *testing.B) { 1312 for i := 0; i < b.N; i++ { 1313 runtime.OsYield() 1314 } 1315 } 1316 1317 func BenchmarkRunningGoProgram(b *testing.B) { 1318 tmpdir := b.TempDir() 1319 1320 src := filepath.Join(tmpdir, "main.go") 1321 err := os.WriteFile(src, []byte(benchmarkRunningGoProgram), 0666) 1322 if err != nil { 1323 b.Fatal(err) 1324 } 1325 1326 exe := filepath.Join(tmpdir, "main.exe") 1327 cmd := exec.Command(testenv.GoToolPath(b), "build", "-o", exe, src) 1328 cmd.Dir = tmpdir 1329 out, err := cmd.CombinedOutput() 1330 if err != nil { 1331 b.Fatalf("building main.exe failed: %v\n%s", err, out) 1332 } 1333 1334 b.ResetTimer() 1335 for i := 0; i < b.N; i++ { 1336 cmd := exec.Command(exe) 1337 out, err := cmd.CombinedOutput() 1338 if err != nil { 1339 b.Fatalf("running main.exe failed: %v\n%s", err, out) 1340 } 1341 } 1342 } 1343 1344 const benchmarkRunningGoProgram = ` 1345 package main 1346 1347 import _ "os" // average Go program will use "os" package, do the same here 1348 1349 func main() { 1350 } 1351 `