github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/runtime/syscall_windows.go (about) 1 // Copyright 2014 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 6 7 import ( 8 "runtime/internal/sys" 9 "unsafe" 10 ) 11 12 // cbs stores all registered Go callbacks. 13 var cbs struct { 14 lock mutex 15 ctxt [cb_max]winCallback 16 index map[winCallbackKey]int 17 n int 18 } 19 20 // winCallback records information about a registered Go callback. 21 type winCallback struct { 22 fn *funcval // Go function 23 retPop uintptr // For 386 cdecl, how many bytes to pop on return 24 25 // abiMap specifies how to translate from a C frame to a Go 26 // frame. This does not specify how to translate back because 27 // the result is always a uintptr. If the C ABI is fastcall, 28 // this assumes the four fastcall registers were first spilled 29 // to the shadow space. 30 abiMap []abiPart 31 // retOffset is the offset of the uintptr-sized result in the Go 32 // frame. 33 retOffset uintptr 34 } 35 36 // abiPart encodes a step in translating between calling ABIs. 37 type abiPart struct { 38 src, dst uintptr 39 len uintptr 40 } 41 42 func (a *abiPart) tryMerge(b abiPart) bool { 43 if a.src+a.len == b.src && a.dst+a.len == b.dst { 44 a.len += b.len 45 return true 46 } 47 return false 48 } 49 50 type winCallbackKey struct { 51 fn *funcval 52 cdecl bool 53 } 54 55 func callbackasm() 56 57 // callbackasmAddr returns address of runtime.callbackasm 58 // function adjusted by i. 59 // On x86 and amd64, runtime.callbackasm is a series of CALL instructions, 60 // and we want callback to arrive at 61 // correspondent call instruction instead of start of 62 // runtime.callbackasm. 63 // On ARM, runtime.callbackasm is a series of mov and branch instructions. 64 // R12 is loaded with the callback index. Each entry is two instructions, 65 // hence 8 bytes. 66 func callbackasmAddr(i int) uintptr { 67 var entrySize int 68 switch GOARCH { 69 default: 70 panic("unsupported architecture") 71 case "386", "amd64": 72 entrySize = 5 73 case "arm": 74 // On ARM, each entry is a MOV instruction 75 // followed by a branch instruction 76 entrySize = 8 77 } 78 return funcPC(callbackasm) + uintptr(i*entrySize) 79 } 80 81 const callbackMaxFrame = 64 * sys.PtrSize 82 83 // compileCallback converts a Go function fn into a C function pointer 84 // that can be passed to Windows APIs. 85 // 86 // On 386, if cdecl is true, the returned C function will use the 87 // cdecl calling convention; otherwise, it will use stdcall. On amd64, 88 // it always uses fastcall. On arm, it always uses the ARM convention. 89 // 90 //go:linkname compileCallback syscall.compileCallback 91 func compileCallback(fn eface, cdecl bool) (code uintptr) { 92 if GOARCH != "386" { 93 // cdecl is only meaningful on 386. 94 cdecl = false 95 } 96 97 if fn._type == nil || (fn._type.kind&kindMask) != kindFunc { 98 panic("compileCallback: expected function with one uintptr-sized result") 99 } 100 ft := (*functype)(unsafe.Pointer(fn._type)) 101 102 // Check arguments and construct ABI translation. 103 var abiMap []abiPart 104 var src, dst uintptr 105 for _, t := range ft.in() { 106 if t.size > sys.PtrSize { 107 // We don't support this right now. In 108 // stdcall/cdecl, 64-bit ints and doubles are 109 // passed as two words (little endian); and 110 // structs are pushed on the stack. In 111 // fastcall, arguments larger than the word 112 // size are passed by reference. On arm, 113 // 8-byte aligned arguments round up to the 114 // next even register and can be split across 115 // registers and the stack. 116 panic("compileCallback: argument size is larger than uintptr") 117 } 118 if k := t.kind & kindMask; (GOARCH == "amd64" || GOARCH == "arm") && (k == kindFloat32 || k == kindFloat64) { 119 // In fastcall, floating-point arguments in 120 // the first four positions are passed in 121 // floating-point registers, which we don't 122 // currently spill. arm passes floating-point 123 // arguments in VFP registers, which we also 124 // don't support. 125 panic("compileCallback: float arguments not supported") 126 } 127 128 // The Go ABI aligns arguments. 129 dst = alignUp(dst, uintptr(t.align)) 130 // In the C ABI, we're already on a word boundary. 131 // Also, sub-word-sized fastcall register arguments 132 // are stored to the least-significant bytes of the 133 // argument word and all supported Windows 134 // architectures are little endian, so src is already 135 // pointing to the right place for smaller arguments. 136 // The same is true on arm. 137 138 // Copy just the size of the argument. Note that this 139 // could be a small by-value struct, but C and Go 140 // struct layouts are compatible, so we can copy these 141 // directly, too. 142 part := abiPart{src, dst, t.size} 143 // Add this step to the adapter. 144 if len(abiMap) == 0 || !abiMap[len(abiMap)-1].tryMerge(part) { 145 abiMap = append(abiMap, part) 146 } 147 148 // cdecl, stdcall, fastcall, and arm pad arguments to word size. 149 src += sys.PtrSize 150 // The Go ABI packs arguments. 151 dst += t.size 152 } 153 // The Go ABI aligns the result to the word size. src is 154 // already aligned. 155 dst = alignUp(dst, sys.PtrSize) 156 retOffset := dst 157 158 if len(ft.out()) != 1 { 159 panic("compileCallback: expected function with one uintptr-sized result") 160 } 161 if ft.out()[0].size != sys.PtrSize { 162 panic("compileCallback: expected function with one uintptr-sized result") 163 } 164 if k := ft.out()[0].kind & kindMask; k == kindFloat32 || k == kindFloat64 { 165 // In cdecl and stdcall, float results are returned in 166 // ST(0). In fastcall, they're returned in XMM0. 167 // Either way, it's not AX. 168 panic("compileCallback: float results not supported") 169 } 170 // Make room for the uintptr-sized result. 171 dst += sys.PtrSize 172 173 if dst > callbackMaxFrame { 174 panic("compileCallback: function argument frame too large") 175 } 176 177 // For cdecl, the callee is responsible for popping its 178 // arguments from the C stack. 179 var retPop uintptr 180 if cdecl { 181 retPop = src 182 } 183 184 key := winCallbackKey{(*funcval)(fn.data), cdecl} 185 186 lock(&cbs.lock) // We don't unlock this in a defer because this is used from the system stack. 187 188 // Check if this callback is already registered. 189 if n, ok := cbs.index[key]; ok { 190 unlock(&cbs.lock) 191 return callbackasmAddr(n) 192 } 193 194 // Register the callback. 195 if cbs.index == nil { 196 cbs.index = make(map[winCallbackKey]int) 197 } 198 n := cbs.n 199 if n >= len(cbs.ctxt) { 200 unlock(&cbs.lock) 201 throw("too many callback functions") 202 } 203 c := winCallback{key.fn, retPop, abiMap, retOffset} 204 cbs.ctxt[n] = c 205 cbs.index[key] = n 206 cbs.n++ 207 208 unlock(&cbs.lock) 209 return callbackasmAddr(n) 210 } 211 212 type callbackArgs struct { 213 index uintptr 214 // args points to the argument block. 215 // 216 // For cdecl and stdcall, all arguments are on the stack. 217 // 218 // For fastcall, the trampoline spills register arguments to 219 // the reserved spill slots below the stack arguments, 220 // resulting in a layout equivalent to stdcall. 221 // 222 // For arm, the trampoline stores the register arguments just 223 // below the stack arguments, so again we can treat it as one 224 // big stack arguments frame. 225 args unsafe.Pointer 226 // Below are out-args from callbackWrap 227 result uintptr 228 retPop uintptr // For 386 cdecl, how many bytes to pop on return 229 } 230 231 // callbackWrap is called by callbackasm to invoke a registered C callback. 232 func callbackWrap(a *callbackArgs) { 233 c := cbs.ctxt[a.index] 234 a.retPop = c.retPop 235 236 // Convert from C to Go ABI. 237 var frame [callbackMaxFrame]byte 238 goArgs := unsafe.Pointer(&frame) 239 for _, part := range c.abiMap { 240 memmove(add(goArgs, part.dst), add(a.args, part.src), part.len) 241 } 242 243 // Even though this is copying back results, we can pass a nil 244 // type because those results must not require write barriers. 245 reflectcall(nil, unsafe.Pointer(c.fn), noescape(goArgs), uint32(c.retOffset)+sys.PtrSize, uint32(c.retOffset)) 246 247 // Extract the result. 248 a.result = *(*uintptr)(unsafe.Pointer(&frame[c.retOffset])) 249 } 250 251 const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 252 253 // When available, this function will use LoadLibraryEx with the filename 254 // parameter and the important SEARCH_SYSTEM32 argument. But on systems that 255 // do not have that option, absoluteFilepath should contain a fallback 256 // to the full path inside of system32 for use with vanilla LoadLibrary. 257 //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary 258 //go:nosplit 259 func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { 260 lockOSThread() 261 c := &getg().m.syscall 262 263 if useLoadLibraryEx { 264 c.fn = getLoadLibraryEx() 265 c.n = 3 266 args := struct { 267 lpFileName *uint16 268 hFile uintptr // always 0 269 flags uint32 270 }{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32} 271 c.args = uintptr(noescape(unsafe.Pointer(&args))) 272 } else { 273 c.fn = getLoadLibrary() 274 c.n = 1 275 c.args = uintptr(noescape(unsafe.Pointer(&absoluteFilepath))) 276 } 277 278 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 279 handle = c.r1 280 if handle == 0 { 281 err = c.err 282 } 283 unlockOSThread() // not defer'd after the lockOSThread above to save stack frame size. 284 return 285 } 286 287 //go:linkname syscall_loadlibrary syscall.loadlibrary 288 //go:nosplit 289 func syscall_loadlibrary(filename *uint16) (handle, err uintptr) { 290 lockOSThread() 291 defer unlockOSThread() 292 c := &getg().m.syscall 293 c.fn = getLoadLibrary() 294 c.n = 1 295 c.args = uintptr(noescape(unsafe.Pointer(&filename))) 296 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 297 handle = c.r1 298 if handle == 0 { 299 err = c.err 300 } 301 return 302 } 303 304 //go:linkname syscall_getprocaddress syscall.getprocaddress 305 //go:nosplit 306 func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uintptr) { 307 lockOSThread() 308 defer unlockOSThread() 309 c := &getg().m.syscall 310 c.fn = getGetProcAddress() 311 c.n = 2 312 c.args = uintptr(noescape(unsafe.Pointer(&handle))) 313 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 314 outhandle = c.r1 315 if outhandle == 0 { 316 err = c.err 317 } 318 return 319 } 320 321 //go:linkname syscall_Syscall syscall.Syscall 322 //go:nosplit 323 func syscall_Syscall(fn, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr) { 324 lockOSThread() 325 defer unlockOSThread() 326 c := &getg().m.syscall 327 c.fn = fn 328 c.n = nargs 329 c.args = uintptr(noescape(unsafe.Pointer(&a1))) 330 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 331 return c.r1, c.r2, c.err 332 } 333 334 //go:linkname syscall_Syscall6 syscall.Syscall6 335 //go:nosplit 336 func syscall_Syscall6(fn, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { 337 lockOSThread() 338 defer unlockOSThread() 339 c := &getg().m.syscall 340 c.fn = fn 341 c.n = nargs 342 c.args = uintptr(noescape(unsafe.Pointer(&a1))) 343 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 344 return c.r1, c.r2, c.err 345 } 346 347 //go:linkname syscall_Syscall9 syscall.Syscall9 348 //go:nosplit 349 func syscall_Syscall9(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { 350 lockOSThread() 351 defer unlockOSThread() 352 c := &getg().m.syscall 353 c.fn = fn 354 c.n = nargs 355 c.args = uintptr(noescape(unsafe.Pointer(&a1))) 356 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 357 return c.r1, c.r2, c.err 358 } 359 360 //go:linkname syscall_Syscall12 syscall.Syscall12 361 //go:nosplit 362 func syscall_Syscall12(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr) { 363 lockOSThread() 364 defer unlockOSThread() 365 c := &getg().m.syscall 366 c.fn = fn 367 c.n = nargs 368 c.args = uintptr(noescape(unsafe.Pointer(&a1))) 369 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 370 return c.r1, c.r2, c.err 371 } 372 373 //go:linkname syscall_Syscall15 syscall.Syscall15 374 //go:nosplit 375 func syscall_Syscall15(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { 376 lockOSThread() 377 defer unlockOSThread() 378 c := &getg().m.syscall 379 c.fn = fn 380 c.n = nargs 381 c.args = uintptr(noescape(unsafe.Pointer(&a1))) 382 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 383 return c.r1, c.r2, c.err 384 } 385 386 //go:linkname syscall_Syscall18 syscall.Syscall18 387 //go:nosplit 388 func syscall_Syscall18(fn, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2, err uintptr) { 389 lockOSThread() 390 defer unlockOSThread() 391 c := &getg().m.syscall 392 c.fn = fn 393 c.n = nargs 394 c.args = uintptr(noescape(unsafe.Pointer(&a1))) 395 cgocall(asmstdcallAddr, unsafe.Pointer(c)) 396 return c.r1, c.r2, c.err 397 }