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