aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/cgocall.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/cgocall.go')
-rw-r--r--src/runtime/cgocall.go166
1 files changed, 62 insertions, 104 deletions
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index 427ed0ffb9..9bca279318 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -35,47 +35,52 @@
// 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 calls crosscall2(_cgoexp_GoF, frame, framesize). Crosscall2
-// (in cgo/gcc_$GOARCH.S, a gcc-compiled assembly file) is a two-argument
-// adapter from the gcc function call ABI to the 6c function call ABI.
-// It is called from gcc to call 6c functions. In this case it calls
-// _cgoexp_GoF(frame, framesize), still running on m->g0's stack
-// and outside the $GOMAXPROCS limit. Thus, this code cannot yet
-// call arbitrary Go code directly and must be careful not to allocate
-// memory or use up m->g0's stack.
+// 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.
//
-// _cgoexp_GoF calls runtime.cgocallback(p.GoF, frame, framesize, ctxt).
-// (The reason for having _cgoexp_GoF instead of writing a crosscall3
-// to make this call directly is that _cgoexp_GoF, because it is compiled
-// with 6c instead of gcc, can refer to dotted names like
-// runtime.cgocallback and p.GoF.)
+// 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(p.GoF, frame, framesize).
-// 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.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
+// 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 p.GoF. 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 p.GoF. Finally it pops but does not execute the deferred
-// function, calls runtime.entersyscall, and returns to runtime.cgocallback.
+// 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 _cgoexp_GoF.
+// 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.
//
-// _cgoexp_GoF immediately returns to crosscall2, which restores the
-// callee-save registers for gcc and returns to GoF, which returns to f.
+// crosscall2 restores the callee-save registers for gcc and returns
+// to GoF, which unpacks any result values and returns to f.
package runtime
@@ -89,6 +94,22 @@ import (
// 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_t
+type argset struct {
+ args unsafe.Pointer
+ retval uintptr
+}
+
+// wrapper for syscall package to call cgocall for libc (cgo) calls.
+//go:linkname syscall_cgocaller syscall.cgocaller
+//go:nosplit
+//go:uintptrescapes
+func syscall_cgocaller(fn unsafe.Pointer, args ...uintptr) uintptr {
+ as := argset{args: unsafe.Pointer(&args[0])}
+ cgocall(fn, unsafe.Pointer(&as))
+ return as.retval
+}
+
// Call from Go to C.
//
// This must be nosplit because it's used for syscalls on some
@@ -176,7 +197,7 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
// Call from C back to Go.
//go:nosplit
-func cgocallbackg(ctxt uintptr) {
+func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
gp := getg()
if gp != gp.m.curg {
println("runtime: bad g in cgocallback")
@@ -204,7 +225,7 @@ func cgocallbackg(ctxt uintptr) {
osPreemptExtExit(gp.m)
- cgocallbackg1(ctxt)
+ cgocallbackg1(fn, frame, ctxt)
// At this point unlockOSThread has been called.
// The following code must not change to a different m.
@@ -219,7 +240,7 @@ func cgocallbackg(ctxt uintptr) {
gp.m.syscall = syscall
}
-func cgocallbackg1(ctxt uintptr) {
+func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) {
gp := getg()
if gp.m.needextram || atomic.Load(&extraMWaiters) > 0 {
gp.m.needextram = false
@@ -263,79 +284,16 @@ func cgocallbackg1(ctxt uintptr) {
raceacquire(unsafe.Pointer(&racecgosync))
}
- type args struct {
- fn *funcval
- arg unsafe.Pointer
- argsize uintptr
- }
- var cb *args
-
- // Location of callback arguments depends on stack frame layout
- // and size of stack frame of cgocallback_gofunc.
- sp := gp.m.g0.sched.sp
- switch GOARCH {
- default:
- throw("cgocallbackg is unimplemented on arch")
- case "arm":
- // On arm, stack frame is two words and there's a saved LR between
- // SP and the stack frame and between the stack frame and the arguments.
- cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
- case "arm64":
- // On arm64, stack frame is four words and there's a saved LR between
- // SP and the stack frame and between the stack frame and the arguments.
- // Additional two words (16-byte alignment) are for saving FP.
- cb = (*args)(unsafe.Pointer(sp + 7*sys.PtrSize))
- case "amd64":
- // On amd64, stack frame is two words, plus caller PC and BP.
- cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
- case "386":
- // On 386, stack frame is three words, plus caller PC.
- cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
- case "ppc64", "ppc64le", "s390x":
- // On ppc64 and s390x, the callback arguments are in the arguments area of
- // cgocallback's stack frame. The stack looks like this:
- // +--------------------+------------------------------+
- // | | ... |
- // | cgoexp_$fn +------------------------------+
- // | | fixed frame area |
- // +--------------------+------------------------------+
- // | | arguments area |
- // | cgocallback +------------------------------+ <- sp + 2*minFrameSize + 2*ptrSize
- // | | fixed frame area |
- // +--------------------+------------------------------+ <- sp + minFrameSize + 2*ptrSize
- // | | local variables (2 pointers) |
- // | cgocallback_gofunc +------------------------------+ <- sp + minFrameSize
- // | | fixed frame area |
- // +--------------------+------------------------------+ <- sp
- cb = (*args)(unsafe.Pointer(sp + 2*sys.MinFrameSize + 2*sys.PtrSize))
- case "mips64", "mips64le":
- // On mips64x, stack frame is two words and there's a saved LR between
- // SP and the stack frame and between the stack frame and the arguments.
- cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
- case "mips", "mipsle":
- // On mipsx, stack frame is two words and there's a saved LR between
- // SP and the stack frame and between the stack frame and the arguments.
- cb = (*args)(unsafe.Pointer(sp + 4*sys.PtrSize))
- }
-
- // Invoke callback.
- // NOTE(rsc): passing nil for argtype means that the copying of the
- // results back into cb.arg happens without any corresponding write barriers.
- // For cgo, cb.arg points into a C stack frame and therefore doesn't
- // hold any pointers that the GC can find anyway - the write barrier
- // would be a no-op.
- reflectcall(nil, unsafe.Pointer(cb.fn), cb.arg, uint32(cb.argsize), 0)
+ // Invoke callback. This function is generated by cmd/cgo and
+ // will unpack the argument frame and call the Go function.
+ var cb func(frame unsafe.Pointer)
+ cbFV := funcval{uintptr(fn)}
+ *(*unsafe.Pointer)(unsafe.Pointer(&cb)) = noescape(unsafe.Pointer(&cbFV))
+ cb(frame)
if raceenabled {
racereleasemerge(unsafe.Pointer(&racecgosync))
}
- if msanenabled {
- // Tell msan that we wrote to the entire argument block.
- // This tells msan that we set the results.
- // Since we have already called the function it doesn't
- // matter that we are writing to the non-result parameters.
- msanwrite(cb.arg, cb.argsize)
- }
// Do not unwind m->g0->sched.sp.
// Our caller, cgocallback, will do that.