Source file src/runtime/syscall_windows.go

     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  	"internal/abi"
     9  	"internal/goarch"
    10  	"unsafe"
    11  )
    12  
    13  // cbs stores all registered Go callbacks.
    14  var cbs struct {
    15  	lock  mutex // use cbsLock / cbsUnlock for race instrumentation.
    16  	ctxt  [cb_max]winCallback
    17  	index map[winCallbackKey]int
    18  	n     int
    19  }
    20  
    21  func cbsLock() {
    22  	lock(&cbs.lock)
    23  	// compileCallback is used by goenvs prior to completion of schedinit.
    24  	// raceacquire involves a racecallback to get the proc, which is not
    25  	// safe prior to scheduler initialization. Thus avoid instrumentation
    26  	// until then.
    27  	if raceenabled && mainStarted {
    28  		raceacquire(unsafe.Pointer(&cbs.lock))
    29  	}
    30  }
    31  
    32  func cbsUnlock() {
    33  	if raceenabled && mainStarted {
    34  		racerelease(unsafe.Pointer(&cbs.lock))
    35  	}
    36  	unlock(&cbs.lock)
    37  }
    38  
    39  // winCallback records information about a registered Go callback.
    40  type winCallback struct {
    41  	fn     *funcval // Go function
    42  	retPop uintptr  // For 386 cdecl, how many bytes to pop on return
    43  	abiMap abiDesc
    44  }
    45  
    46  // abiPartKind is the action an abiPart should take.
    47  type abiPartKind int
    48  
    49  const (
    50  	abiPartBad   abiPartKind = iota
    51  	abiPartStack             // Move a value from memory to the stack.
    52  	abiPartReg               // Move a value from memory to a register.
    53  )
    54  
    55  // abiPart encodes a step in translating between calling ABIs.
    56  type abiPart struct {
    57  	kind           abiPartKind
    58  	srcStackOffset uintptr
    59  	dstStackOffset uintptr // used if kind == abiPartStack
    60  	dstRegister    int     // used if kind == abiPartReg
    61  	len            uintptr
    62  }
    63  
    64  func (a *abiPart) tryMerge(b abiPart) bool {
    65  	if a.kind != abiPartStack || b.kind != abiPartStack {
    66  		return false
    67  	}
    68  	if a.srcStackOffset+a.len == b.srcStackOffset && a.dstStackOffset+a.len == b.dstStackOffset {
    69  		a.len += b.len
    70  		return true
    71  	}
    72  	return false
    73  }
    74  
    75  // abiDesc specifies how to translate from a C frame to a Go
    76  // frame. This does not specify how to translate back because
    77  // the result is always a uintptr. If the C ABI is fastcall,
    78  // this assumes the four fastcall registers were first spilled
    79  // to the shadow space.
    80  type abiDesc struct {
    81  	parts []abiPart
    82  
    83  	srcStackSize uintptr // stdcall/fastcall stack space tracking
    84  	dstStackSize uintptr // Go stack space used
    85  	dstSpill     uintptr // Extra stack space for argument spill slots
    86  	dstRegisters int     // Go ABI int argument registers used
    87  
    88  	// retOffset is the offset of the uintptr-sized result in the Go
    89  	// frame.
    90  	retOffset uintptr
    91  }
    92  
    93  func (p *abiDesc) assignArg(t *_type) {
    94  	if t.Size_ > goarch.PtrSize {
    95  		// We don't support this right now. In
    96  		// stdcall/cdecl, 64-bit ints and doubles are
    97  		// passed as two words (little endian); and
    98  		// structs are pushed on the stack. In
    99  		// fastcall, arguments larger than the word
   100  		// size are passed by reference.
   101  		panic("compileCallback: argument size is larger than uintptr")
   102  	}
   103  	if k := t.Kind(); GOARCH != "386" && (k == abi.Float32 || k == abi.Float64) {
   104  		// In fastcall, floating-point arguments in
   105  		// the first four positions are passed in
   106  		// floating-point registers, which we don't
   107  		// currently spill.
   108  		// So basically we only support 386.
   109  		panic("compileCallback: float arguments not supported")
   110  	}
   111  
   112  	if t.Size_ == 0 {
   113  		// The Go ABI aligns for zero-sized types.
   114  		p.dstStackSize = alignUp(p.dstStackSize, uintptr(t.Align_))
   115  		return
   116  	}
   117  
   118  	// In the C ABI, we're already on a word boundary.
   119  	// Also, sub-word-sized fastcall register arguments
   120  	// are stored to the least-significant bytes of the
   121  	// argument word and all supported Windows
   122  	// architectures are little endian, so srcStackOffset
   123  	// is already pointing to the right place for smaller
   124  	// arguments.
   125  
   126  	oldParts := p.parts
   127  	if p.tryRegAssignArg(t, 0) {
   128  		// Account for spill space.
   129  		//
   130  		// TODO(mknyszek): Remove this when we no longer have
   131  		// caller reserved spill space.
   132  		p.dstSpill = alignUp(p.dstSpill, uintptr(t.Align_))
   133  		p.dstSpill += t.Size_
   134  	} else {
   135  		// Register assignment failed.
   136  		// Undo the work and stack assign.
   137  		p.parts = oldParts
   138  
   139  		// The Go ABI aligns arguments.
   140  		p.dstStackSize = alignUp(p.dstStackSize, uintptr(t.Align_))
   141  
   142  		// Copy just the size of the argument. Note that this
   143  		// could be a small by-value struct, but C and Go
   144  		// struct layouts are compatible, so we can copy these
   145  		// directly, too.
   146  		part := abiPart{
   147  			kind:           abiPartStack,
   148  			srcStackOffset: p.srcStackSize,
   149  			dstStackOffset: p.dstStackSize,
   150  			len:            t.Size_,
   151  		}
   152  		// Add this step to the adapter.
   153  		if len(p.parts) == 0 || !p.parts[len(p.parts)-1].tryMerge(part) {
   154  			p.parts = append(p.parts, part)
   155  		}
   156  		// The Go ABI packs arguments.
   157  		p.dstStackSize += t.Size_
   158  	}
   159  
   160  	// cdecl, stdcall, and fastcall pad arguments to word size.
   161  	// TODO(rsc): On arm64 do we need to skip the caller's saved LR?
   162  	p.srcStackSize += goarch.PtrSize
   163  }
   164  
   165  // tryRegAssignArg tries to register-assign a value of type t.
   166  // If this type is nested in an aggregate type, then offset is the
   167  // offset of this type within its parent type.
   168  // Assumes t.size <= goarch.PtrSize and t.size != 0.
   169  //
   170  // Returns whether the assignment succeeded.
   171  func (p *abiDesc) tryRegAssignArg(t *_type, offset uintptr) bool {
   172  	switch k := t.Kind(); k {
   173  	case abi.Bool, abi.Int, abi.Int8, abi.Int16, abi.Int32, abi.Uint, abi.Uint8, abi.Uint16, abi.Uint32, abi.Uintptr, abi.Pointer, abi.UnsafePointer:
   174  		// Assign a register for all these types.
   175  		return p.assignReg(t.Size_, offset)
   176  	case abi.Int64, abi.Uint64:
   177  		// Only register-assign if the registers are big enough.
   178  		if goarch.PtrSize == 8 {
   179  			return p.assignReg(t.Size_, offset)
   180  		}
   181  	case abi.Array:
   182  		at := (*arraytype)(unsafe.Pointer(t))
   183  		if at.Len == 1 {
   184  			return p.tryRegAssignArg(at.Elem, offset) // TODO fix when runtime is fully commoned up w/ abi.Type
   185  		}
   186  	case abi.Struct:
   187  		st := (*structtype)(unsafe.Pointer(t))
   188  		for i := range st.Fields {
   189  			f := &st.Fields[i]
   190  			if !p.tryRegAssignArg(f.Typ, offset+f.Offset) {
   191  				return false
   192  			}
   193  		}
   194  		return true
   195  	}
   196  	// Pointer-sized types such as maps and channels are currently
   197  	// not supported.
   198  	panic("compileCallback: type " + toRType(t).string() + " is currently not supported for use in system callbacks")
   199  }
   200  
   201  // assignReg attempts to assign a single register for an
   202  // argument with the given size, at the given offset into the
   203  // value in the C ABI space.
   204  //
   205  // Returns whether the assignment was successful.
   206  func (p *abiDesc) assignReg(size, offset uintptr) bool {
   207  	if p.dstRegisters >= intArgRegs {
   208  		return false
   209  	}
   210  	p.parts = append(p.parts, abiPart{
   211  		kind:           abiPartReg,
   212  		srcStackOffset: p.srcStackSize + offset,
   213  		dstRegister:    p.dstRegisters,
   214  		len:            size,
   215  	})
   216  	p.dstRegisters++
   217  	return true
   218  }
   219  
   220  type winCallbackKey struct {
   221  	fn    *funcval
   222  	cdecl bool
   223  }
   224  
   225  func callbackasm()
   226  
   227  // callbackasmAddr returns address of runtime.callbackasm
   228  // function adjusted by i.
   229  // On x86 and amd64, runtime.callbackasm is a series of CALL instructions,
   230  // and we want callback to arrive at
   231  // correspondent call instruction instead of start of
   232  // runtime.callbackasm.
   233  // On ARM64, runtime.callbackasm is a series of mov and branch instructions.
   234  // R12 is loaded with the callback index. Each entry is two instructions,
   235  // hence 8 bytes.
   236  func callbackasmAddr(i int) uintptr {
   237  	var entrySize int
   238  	switch GOARCH {
   239  	default:
   240  		panic("unsupported architecture")
   241  	case "386", "amd64":
   242  		entrySize = 5
   243  	case "arm64":
   244  		// On ARM64, each entry is a MOV instruction
   245  		// followed by a branch instruction
   246  		entrySize = 8
   247  	}
   248  	return abi.FuncPCABI0(callbackasm) + uintptr(i*entrySize)
   249  }
   250  
   251  const callbackMaxFrame = 64 * goarch.PtrSize
   252  
   253  // compileCallback converts a Go function fn into a C function pointer
   254  // that can be passed to Windows APIs.
   255  //
   256  // On 386, if cdecl is true, the returned C function will use the
   257  // cdecl calling convention; otherwise, it will use stdcall. On amd64,
   258  // it always uses fastcall.
   259  //
   260  //go:linkname compileCallback syscall.compileCallback
   261  func compileCallback(fn eface, cdecl bool) (code uintptr) {
   262  	if GOARCH != "386" {
   263  		// cdecl is only meaningful on 386.
   264  		cdecl = false
   265  	}
   266  
   267  	if fn._type == nil || fn._type.Kind() != abi.Func {
   268  		panic("compileCallback: expected function with one uintptr-sized result")
   269  	}
   270  	ft := (*functype)(unsafe.Pointer(fn._type))
   271  
   272  	// Check arguments and construct ABI translation.
   273  	var abiMap abiDesc
   274  	for _, t := range ft.InSlice() {
   275  		abiMap.assignArg(t)
   276  	}
   277  	// The Go ABI aligns the result to the word size. src is
   278  	// already aligned.
   279  	abiMap.dstStackSize = alignUp(abiMap.dstStackSize, goarch.PtrSize)
   280  	abiMap.retOffset = abiMap.dstStackSize
   281  
   282  	if len(ft.OutSlice()) != 1 {
   283  		panic("compileCallback: expected function with one uintptr-sized result")
   284  	}
   285  	if ft.OutSlice()[0].Size_ != goarch.PtrSize {
   286  		panic("compileCallback: expected function with one uintptr-sized result")
   287  	}
   288  	if k := ft.OutSlice()[0].Kind(); k == abi.Float32 || k == abi.Float64 {
   289  		// In cdecl and stdcall, float results are returned in
   290  		// ST(0). In fastcall, they're returned in XMM0.
   291  		// Either way, it's not AX.
   292  		panic("compileCallback: float results not supported")
   293  	}
   294  	if intArgRegs == 0 {
   295  		// Make room for the uintptr-sized result.
   296  		// If there are argument registers, the return value will
   297  		// be passed in the first register.
   298  		abiMap.dstStackSize += goarch.PtrSize
   299  	}
   300  
   301  	// TODO(mknyszek): Remove dstSpill from this calculation when we no longer have
   302  	// caller reserved spill space.
   303  	frameSize := alignUp(abiMap.dstStackSize, goarch.PtrSize)
   304  	frameSize += abiMap.dstSpill
   305  	if frameSize > callbackMaxFrame {
   306  		panic("compileCallback: function argument frame too large")
   307  	}
   308  
   309  	// For cdecl, the callee is responsible for popping its
   310  	// arguments from the C stack.
   311  	var retPop uintptr
   312  	if cdecl {
   313  		retPop = abiMap.srcStackSize
   314  	}
   315  
   316  	key := winCallbackKey{(*funcval)(fn.data), cdecl}
   317  
   318  	cbsLock()
   319  
   320  	// Check if this callback is already registered.
   321  	if n, ok := cbs.index[key]; ok {
   322  		cbsUnlock()
   323  		return callbackasmAddr(n)
   324  	}
   325  
   326  	// Register the callback.
   327  	if cbs.index == nil {
   328  		cbs.index = make(map[winCallbackKey]int)
   329  	}
   330  	n := cbs.n
   331  	if n >= len(cbs.ctxt) {
   332  		cbsUnlock()
   333  		throw("too many callback functions")
   334  	}
   335  	c := winCallback{key.fn, retPop, abiMap}
   336  	cbs.ctxt[n] = c
   337  	cbs.index[key] = n
   338  	cbs.n++
   339  
   340  	cbsUnlock()
   341  	return callbackasmAddr(n)
   342  }
   343  
   344  type callbackArgs struct {
   345  	index uintptr
   346  	// args points to the argument block.
   347  	//
   348  	// For cdecl and stdcall, all arguments are on the stack.
   349  	//
   350  	// For fastcall, the trampoline spills register arguments to
   351  	// the reserved spill slots below the stack arguments,
   352  	// resulting in a layout equivalent to stdcall.
   353  	args unsafe.Pointer
   354  	// Below are out-args from callbackWrap
   355  	result uintptr
   356  	retPop uintptr // For 386 cdecl, how many bytes to pop on return
   357  }
   358  
   359  // callbackWrap is called by callbackasm to invoke a registered C callback.
   360  func callbackWrap(a *callbackArgs) {
   361  	c := cbs.ctxt[a.index]
   362  	a.retPop = c.retPop
   363  
   364  	// Convert from C to Go ABI.
   365  	var regs abi.RegArgs
   366  	var frame [callbackMaxFrame]byte
   367  	goArgs := unsafe.Pointer(&frame)
   368  	for _, part := range c.abiMap.parts {
   369  		switch part.kind {
   370  		case abiPartStack:
   371  			memmove(add(goArgs, part.dstStackOffset), add(a.args, part.srcStackOffset), part.len)
   372  		case abiPartReg:
   373  			goReg := unsafe.Pointer(&regs.Ints[part.dstRegister])
   374  			memmove(goReg, add(a.args, part.srcStackOffset), part.len)
   375  		default:
   376  			panic("bad ABI description")
   377  		}
   378  	}
   379  
   380  	// TODO(mknyszek): Remove this when we no longer have
   381  	// caller reserved spill space.
   382  	frameSize := alignUp(c.abiMap.dstStackSize, goarch.PtrSize)
   383  	frameSize += c.abiMap.dstSpill
   384  
   385  	// Even though this is copying back results, we can pass a nil
   386  	// type because those results must not require write barriers.
   387  	reflectcall(nil, unsafe.Pointer(c.fn), noescape(goArgs), uint32(c.abiMap.dstStackSize), uint32(c.abiMap.retOffset), uint32(frameSize), &regs)
   388  
   389  	// Extract the result.
   390  	//
   391  	// There's always exactly one return value, one pointer in size.
   392  	// If it's on the stack, then we will have reserved space for it
   393  	// at the end of the frame, otherwise it was passed in a register.
   394  	if c.abiMap.dstStackSize != c.abiMap.retOffset {
   395  		a.result = *(*uintptr)(unsafe.Pointer(&frame[c.abiMap.retOffset]))
   396  	} else {
   397  		var zero int
   398  		// On architectures with no registers, Ints[0] would be a compile error,
   399  		// so we use a dynamic index. These architectures will never take this
   400  		// branch, so this won't cause a runtime panic.
   401  		a.result = regs.Ints[zero]
   402  	}
   403  }
   404  
   405  // syscall_syscalln calls fn with args[:n].
   406  // It is used to implement [syscall.SyscallN].
   407  // It shouldn't be used in the runtime package,
   408  // use [stdcall] instead.
   409  //
   410  //go:linkname syscall_syscalln syscall.syscalln
   411  //go:nosplit
   412  //go:uintptrkeepalive
   413  func syscall_syscalln(fn, n uintptr, args ...uintptr) (r1, r2, err uintptr) {
   414  	if n > uintptr(len(args)) {
   415  		panic("syscall: n > len(args)") // should not be reachable from user code
   416  	}
   417  
   418  	// The cgocall parameters are stored in m instead of in
   419  	// the stack because the stack can move during fn if it
   420  	// calls back into Go.
   421  	c := &getg().m.winsyscall
   422  	c.Fn = fn
   423  	c.N = n
   424  	if c.N != 0 {
   425  		c.Args = uintptr(noescape(unsafe.Pointer(&args[0])))
   426  	}
   427  	errno := cgocall(asmstdcallAddr, unsafe.Pointer(c))
   428  	// cgocall may reschedule us on to a different M,
   429  	// but it copies the return values into the new M's
   430  	// so we can read them from there.
   431  	c = &getg().m.winsyscall
   432  	return c.R1, c.R2, uintptr(uint32(errno))
   433  }
   434  

View as plain text