Source File
cgocall.go
Belonging Package
runtime
// Copyright 2009 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// Cgo call and callback support.//// To call into the C function f from Go, the cgo-generated code calls// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a// gcc-compiled function written by cgo.//// runtime.cgocall (below) calls entersyscall so as not to block// other goroutines or the garbage collector, and then calls// runtime.asmcgocall(_cgo_Cfunc_f, frame).//// runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack// (assumed to be an operating system-allocated stack, so safe to run// gcc-compiled code on) and calls _cgo_Cfunc_f(frame).//// _cgo_Cfunc_f invokes the actual C function f with arguments// taken from the frame structure, records the results in the frame,// and returns to runtime.asmcgocall.//// After it regains control, runtime.asmcgocall switches back to the// original g (m->curg)'s stack and returns to runtime.cgocall.//// After it regains control, runtime.cgocall calls exitsyscall, which blocks// until this m can run Go code without violating the $GOMAXPROCS limit,// and then unlocks g from m.//// The above description skipped over the possibility of the gcc-compiled// function f calling back into Go. If that happens, we continue down// the rabbit hole during the execution of f.//// To make it possible for gcc-compiled C code to call a Go function p.GoF,// cgo writes a gcc-compiled function named GoF (not p.GoF, since gcc doesn't// know about packages). The gcc-compiled C function f calls GoF.//// GoF initializes "frame", a structure containing all of its// arguments and slots for p.GoF's results. It calls// crosscall2(_cgoexp_GoF, frame, framesize, ctxt) using the gcc ABI.//// crosscall2 (in cgo/asm_$GOARCH.s) is a four-argument adapter from// the gcc function call ABI to the gc function call ABI. At this// point we're in the Go runtime, but we're still running on m.g0's// stack and outside the $GOMAXPROCS limit. crosscall2 calls// runtime.cgocallback(_cgoexp_GoF, frame, ctxt) using the gc ABI.// (crosscall2's framesize argument is no longer used, but there's one// case where SWIG calls crosscall2 directly and expects to pass this// argument. See _cgo_panic.)//// runtime.cgocallback (in asm_$GOARCH.s) switches from m.g0's stack// to the original g (m.curg)'s stack, on which it calls// runtime.cgocallbackg(_cgoexp_GoF, frame, ctxt). As part of the// stack switch, runtime.cgocallback saves the current SP as// m.g0.sched.sp, so that any use of m.g0's stack during the execution// of the callback will be done below the existing stack frames.// Before overwriting m.g0.sched.sp, it pushes the old value on the// m.g0 stack, so that it can be restored later.//// runtime.cgocallbackg (below) is now running on a real goroutine// stack (not an m.g0 stack). First it calls runtime.exitsyscall, which will// block until the $GOMAXPROCS limit allows running this goroutine.// Once exitsyscall has returned, it is safe to do things like call the memory// allocator or invoke the Go callback function. runtime.cgocallbackg// first defers a function to unwind m.g0.sched.sp, so that if p.GoF// panics, m.g0.sched.sp will be restored to its old value: the m.g0 stack// and the m.curg stack will be unwound in lock step.// Then it calls _cgoexp_GoF(frame).//// _cgoexp_GoF, which was generated by cmd/cgo, unpacks the arguments// from frame, calls p.GoF, writes the results back to frame, and// returns. Now we start unwinding this whole process.//// runtime.cgocallbackg pops but does not execute the deferred// function to unwind m.g0.sched.sp, calls runtime.entersyscall, and// returns to runtime.cgocallback.//// After it regains control, runtime.cgocallback switches back to// m.g0's stack (the pointer is still in m.g0.sched.sp), restores the old// m.g0.sched.sp value from the stack, and returns to crosscall2.//// crosscall2 restores the callee-save registers for gcc and returns// to GoF, which unpacks any result values and returns to f.package runtimeimport ()// Addresses collected in a cgo backtrace when crashing.// Length must match arg.Max in x_cgo_callers in runtime/cgo/gcc_traceback.c.type cgoCallers [32]uintptr// argset matches runtime/cgo/linux_syscall.c:argset_ttype argset struct {args unsafe.Pointerretval uintptr}// wrapper for syscall package to call cgocall for libc (cgo) calls.//go:linkname syscall_cgocaller syscall.cgocaller//go:nosplit//go:uintptrescapesfunc ( unsafe.Pointer, ...uintptr) uintptr {:= argset{args: unsafe.Pointer(&[0])}cgocall(, unsafe.Pointer(&))return .retval}// Call from Go to C.//// This must be nosplit because it's used for syscalls on some// platforms. Syscalls may have untyped arguments on the stack, so// it's not safe to grow or scan the stack.////go:nosplitfunc (, unsafe.Pointer) int32 {if !iscgo && GOOS != "solaris" && GOOS != "illumos" && GOOS != "windows" {throw("cgocall unavailable")}if == nil {throw("cgocall nil")}if raceenabled {racereleasemerge(unsafe.Pointer(&racecgosync))}:= getg().m.ncgocall++.ncgo++// Reset traceback..cgoCallers[0] = 0// Announce we are entering a system call// so that the scheduler knows to create another// M to run goroutines while we are in the// foreign code.//// The call to asmcgocall is guaranteed not to// grow the stack and does not allocate memory,// so it is safe to call while "in a system call", outside// the $GOMAXPROCS accounting.//// fn may call back into Go code, in which case we'll exit the// "system call", run the Go code (which may grow the stack),// and then re-enter the "system call" reusing the PC and SP// saved by entersyscall here.entersyscall()// Tell asynchronous preemption that we're entering external// code. We do this after entersyscall because this may block// and cause an async preemption to fail, but at this point a// sync preemption will succeed (though this is not a matter// of correctness).osPreemptExtEnter().incgo = true:= asmcgocall(, )// Update accounting before exitsyscall because exitsyscall may// reschedule us on to a different M..incgo = false.ncgo--osPreemptExtExit()exitsyscall()// Note that raceacquire must be called only after exitsyscall has// wired this M to a P.if raceenabled {raceacquire(unsafe.Pointer(&racecgosync))}// From the garbage collector's perspective, time can move// backwards in the sequence above. If there's a callback into// Go code, GC will see this function at the call to// asmcgocall. When the Go call later returns to C, the// syscall PC/SP is rolled back and the GC sees this function// back at the call to entersyscall. Normally, fn and arg// would be live at entersyscall and dead at asmcgocall, so if// time moved backwards, GC would see these arguments as dead// and then live. Prevent these undead arguments from crashing// GC by forcing them to stay live across this time warp.KeepAlive()KeepAlive()KeepAlive()return}// Call from C back to Go.//go:nosplitfunc (, unsafe.Pointer, uintptr) {:= getg()if != .m.curg {println("runtime: bad g in cgocallback")exit(2)}// The call from C is on gp.m's g0 stack, so we must ensure// that we stay on that M. We have to do this before calling// exitsyscall, since it would otherwise be free to move us to// a different M. The call to unlockOSThread is in unwindm.lockOSThread()// Save current syscall parameters, so m.syscall can be// used again if callback decide to make syscall.:= .m.syscall// entersyscall saves the caller's SP to allow the GC to trace the Go// stack. However, since we're returning to an earlier stack frame and// need to pair with the entersyscall() call made by cgocall, we must// save syscall* and let reentersyscall restore them.:= unsafe.Pointer(.syscallsp):= .syscallpcexitsyscall() // coming out of cgo call.m.incgo = falseosPreemptExtExit(.m)cgocallbackg1(, , )// At this point unlockOSThread has been called.// The following code must not change to a different m.// This is enforced by checking incgo in the schedule function.osPreemptExtEnter(.m).m.incgo = true// going back to cgo callreentersyscall(, uintptr()).m.syscall =}func (, unsafe.Pointer, uintptr) {:= getg()if .m.needextram || atomic.Load(&extraMWaiters) > 0 {.m.needextram = falsesystemstack(newextram)}if != 0 {:= append(.cgoCtxt, )// Now we need to set gp.cgoCtxt = s, but we could get// a SIGPROF signal while manipulating the slice, and// the SIGPROF handler could pick up gp.cgoCtxt while// tracing up the stack. We need to ensure that the// handler always sees a valid slice, so set the// values in an order such that it always does.:= (*slice)(unsafe.Pointer(&.cgoCtxt))atomicstorep(unsafe.Pointer(&.array), unsafe.Pointer(&[0])).cap = cap().len = len()defer func( *g) {// Decrease the length of the slice by one, safely.:= (*slice)(unsafe.Pointer(&.cgoCtxt)).len--}()}if .m.ncgo == 0 {// The C call to Go came from a thread not currently running// any Go. In the case of -buildmode=c-archive or c-shared,// this call may be coming in before package initialization// is complete. Wait until it is.<-main_init_done}// Add entry to defer stack in case of panic.:= truedefer unwindm(&)if raceenabled {raceacquire(unsafe.Pointer(&racecgosync))}// Invoke callback. This function is generated by cmd/cgo and// will unpack the argument frame and call the Go function.var func( unsafe.Pointer):= funcval{uintptr()}*(*unsafe.Pointer)(unsafe.Pointer(&)) = noescape(unsafe.Pointer(&))()if raceenabled {racereleasemerge(unsafe.Pointer(&racecgosync))}// Do not unwind m->g0->sched.sp.// Our caller, cgocallback, will do that.= false}func ( *bool) {if * {// Restore sp saved by cgocallback during// unwind of g's stack (see comment at top of file).:= acquirem():= &.g0.schedswitch GOARCH {default:throw("unwindm not implemented")case "386", "amd64", "arm", "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "mips", "mipsle", "riscv64":.sp = *(*uintptr)(unsafe.Pointer(.sp + sys.MinFrameSize))case "arm64":.sp = *(*uintptr)(unsafe.Pointer(.sp + 16))}// Do the accounting that cgocall will not have a chance to do// during an unwind.//// In the case where a Go call originates from C, ncgo is 0// and there is no matching cgocall to end.if .ncgo > 0 {.incgo = false.ncgo--osPreemptExtExit()}releasem()}// Undo the call to lockOSThread in cgocallbackg.// We must still stay on the same m.unlockOSThread()}// called from assemblyfunc () {throw("misaligned stack in cgocallback")}// called from (incomplete) assemblyfunc () {throw("cgo not implemented")}var racecgosync uint64 // represents possible synchronization in C code// Pointer checking for cgo code.// We want to detect all cases where a program that does not use// unsafe makes a cgo call passing a Go pointer to memory that// contains a Go pointer. Here a Go pointer is defined as a pointer// to memory allocated by the Go runtime. Programs that use unsafe// can evade this restriction easily, so we don't try to catch them.// The cgo program will rewrite all possibly bad pointer arguments to// call cgoCheckPointer, where we can catch cases of a Go pointer// pointing to a Go pointer.// Complicating matters, taking the address of a slice or array// element permits the C program to access all elements of the slice// or array. In that case we will see a pointer to a single element,// but we need to check the entire data structure.// The cgoCheckPointer call takes additional arguments indicating that// it was called on an address expression. An additional argument of// true means that it only needs to check a single element. An// additional argument of a slice or array means that it needs to// check the entire slice/array, but nothing else. Otherwise, the// pointer could be anything, and we check the entire heap object,// which is conservative but safe.// When and if we implement a moving garbage collector,// cgoCheckPointer will pin the pointer for the duration of the cgo// call. (This is necessary but not sufficient; the cgo program will// also have to change to pin Go pointers that cannot point to Go// pointers.)// cgoCheckPointer checks if the argument contains a Go pointer that// points to a Go pointer, and panics if it does.func ( interface{}, interface{}) {if debug.cgocheck == 0 {return}:= efaceOf(&):= ._type:= trueif != nil && (.kind&kindMask == kindPtr || .kind&kindMask == kindUnsafePointer) {:= .dataif .kind&kindDirectIface == 0 {= *(*unsafe.Pointer)()}if == nil || !cgoIsGoPointer() {return}:= efaceOf(&)switch ._type.kind & kindMask {case kindBool:if .kind&kindMask == kindUnsafePointer {// We don't know the type of the element.break}:= (*ptrtype)(unsafe.Pointer())cgoCheckArg(.elem, , true, false, cgoCheckPointerFail)returncase kindSlice:// Check the slice rather than the pointer.== ._typecase kindArray:// Check the array rather than the pointer.// Pass top as false since we have a pointer// to the array.== ._type= falsedefault:throw("can't happen")}}cgoCheckArg(, .data, .kind&kindDirectIface == 0, , cgoCheckPointerFail)}const cgoCheckPointerFail = "cgo argument has Go pointer to Go pointer"const cgoResultFail = "cgo result has Go pointer"// cgoCheckArg is the real work of cgoCheckPointer. The argument p// is either a pointer to the value (of type t), or the value itself,// depending on indir. The top parameter is whether we are at the top// level, where Go pointers are allowed.func ( *_type, unsafe.Pointer, , bool, string) {if .ptrdata == 0 || == nil {// If the type has no pointers there is nothing to do.return}switch .kind & kindMask {default:throw("can't happen")case kindArray::= (*arraytype)(unsafe.Pointer())if ! {if .len != 1 {throw("can't happen")}(.elem, , .elem.kind&kindDirectIface == 0, , )return}for := uintptr(0); < .len; ++ {(.elem, , true, , )= add(, .elem.size)}case kindChan, kindMap:// These types contain internal pointers that will// always be allocated in the Go heap. It's never OK// to pass them to C.panic(errorString())case kindFunc:if {= *(*unsafe.Pointer)()}if !cgoIsGoPointer() {return}panic(errorString())case kindInterface::= *(**_type)()if == nil {return}// A type known at compile time is OK since it's// constant. A type not known at compile time will be// in the heap and will not be OK.if inheap(uintptr(unsafe.Pointer())) {panic(errorString())}= *(*unsafe.Pointer)(add(, sys.PtrSize))if !cgoIsGoPointer() {return}if ! {panic(errorString())}(, , .kind&kindDirectIface == 0, false, )case kindSlice::= (*slicetype)(unsafe.Pointer()):= (*slice)()= .arrayif == nil || !cgoIsGoPointer() {return}if ! {panic(errorString())}if .elem.ptrdata == 0 {return}for := 0; < .cap; ++ {(.elem, , true, false, )= add(, .elem.size)}case kindString::= (*stringStruct)()if !cgoIsGoPointer(.str) {return}if ! {panic(errorString())}case kindStruct::= (*structtype)(unsafe.Pointer())if ! {if len(.fields) != 1 {throw("can't happen")}(.fields[0].typ, , .fields[0].typ.kind&kindDirectIface == 0, , )return}for , := range .fields {if .typ.ptrdata == 0 {continue}(.typ, add(, .offset()), true, , )}case kindPtr, kindUnsafePointer:if {= *(*unsafe.Pointer)()if == nil {return}}if !cgoIsGoPointer() {return}if ! {panic(errorString())}cgoCheckUnknownPointer(, )}}// cgoCheckUnknownPointer is called for an arbitrary pointer into Go// memory. It checks whether that Go memory contains any other// pointer into Go memory. If it does, we panic.// The return values are unused but useful to see in panic tracebacks.func ( unsafe.Pointer, string) (, uintptr) {if inheap(uintptr()) {, , := findObject(uintptr(), 0, 0)=if == 0 {return}:= heapBitsForAddr():= .elemsizefor = uintptr(0); < ; += sys.PtrSize {if !.morePointers() {// No more possible pointers.break}if .isPointer() && cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer( + ))) {panic(errorString())}= .next()}return}for , := range activeModules() {if cgoInRange(, .data, .edata) || cgoInRange(, .bss, .ebss) {// We have no way to know the size of the object.// We have to assume that it might contain a pointer.panic(errorString())}// In the text or noptr sections, we know that the// pointer does not point to a Go pointer.}return}// cgoIsGoPointer reports whether the pointer is a Go pointer--a// pointer to Go memory. We only care about Go memory that might// contain pointers.//go:nosplit//go:nowritebarrierrecfunc ( unsafe.Pointer) bool {if == nil {return false}if inHeapOrStack(uintptr()) {return true}for , := range activeModules() {if cgoInRange(, .data, .edata) || cgoInRange(, .bss, .ebss) {return true}}return false}// cgoInRange reports whether p is between start and end.//go:nosplit//go:nowritebarrierrecfunc ( unsafe.Pointer, , uintptr) bool {return <= uintptr() && uintptr() <}// cgoCheckResult is called to check the result parameter of an// exported Go function. It panics if the result is or contains a Go// pointer.func ( interface{}) {if debug.cgocheck == 0 {return}:= efaceOf(&):= ._typecgoCheckArg(, .data, .kind&kindDirectIface == 0, false, cgoResultFail)}