rsc.io/go@v0.0.0-20150416155037-e040fd465409/src/runtime/cgocall.go (about) 1 // Copyright 2009 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 // Cgo call and callback support. 6 // 7 // To call into the C function f from Go, the cgo-generated code calls 8 // runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a 9 // gcc-compiled function written by cgo. 10 // 11 // runtime.cgocall (below) locks g to m, calls entersyscall 12 // so as not to block other goroutines or the garbage collector, 13 // and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame). 14 // 15 // runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack 16 // (assumed to be an operating system-allocated stack, so safe to run 17 // gcc-compiled code on) and calls _cgo_Cfunc_f(frame). 18 // 19 // _cgo_Cfunc_f invokes the actual C function f with arguments 20 // taken from the frame structure, records the results in the frame, 21 // and returns to runtime.asmcgocall. 22 // 23 // After it regains control, runtime.asmcgocall switches back to the 24 // original g (m->curg)'s stack and returns to runtime.cgocall. 25 // 26 // After it regains control, runtime.cgocall calls exitsyscall, which blocks 27 // until this m can run Go code without violating the $GOMAXPROCS limit, 28 // and then unlocks g from m. 29 // 30 // The above description skipped over the possibility of the gcc-compiled 31 // function f calling back into Go. If that happens, we continue down 32 // the rabbit hole during the execution of f. 33 // 34 // To make it possible for gcc-compiled C code to call a Go function p.GoF, 35 // cgo writes a gcc-compiled function named GoF (not p.GoF, since gcc doesn't 36 // know about packages). The gcc-compiled C function f calls GoF. 37 // 38 // GoF calls crosscall2(_cgoexp_GoF, frame, framesize). Crosscall2 39 // (in cgo/gcc_$GOARCH.S, a gcc-compiled assembly file) is a two-argument 40 // adapter from the gcc function call ABI to the 6c function call ABI. 41 // It is called from gcc to call 6c functions. In this case it calls 42 // _cgoexp_GoF(frame, framesize), still running on m->g0's stack 43 // and outside the $GOMAXPROCS limit. Thus, this code cannot yet 44 // call arbitrary Go code directly and must be careful not to allocate 45 // memory or use up m->g0's stack. 46 // 47 // _cgoexp_GoF calls runtime.cgocallback(p.GoF, frame, framesize). 48 // (The reason for having _cgoexp_GoF instead of writing a crosscall3 49 // to make this call directly is that _cgoexp_GoF, because it is compiled 50 // with 6c instead of gcc, can refer to dotted names like 51 // runtime.cgocallback and p.GoF.) 52 // 53 // runtime.cgocallback (in asm_$GOARCH.s) switches from m->g0's 54 // stack to the original g (m->curg)'s stack, on which it calls 55 // runtime.cgocallbackg(p.GoF, frame, framesize). 56 // As part of the stack switch, runtime.cgocallback saves the current 57 // SP as m->g0->sched.sp, so that any use of m->g0's stack during the 58 // execution of the callback will be done below the existing stack frames. 59 // Before overwriting m->g0->sched.sp, it pushes the old value on the 60 // m->g0 stack, so that it can be restored later. 61 // 62 // runtime.cgocallbackg (below) is now running on a real goroutine 63 // stack (not an m->g0 stack). First it calls runtime.exitsyscall, which will 64 // block until the $GOMAXPROCS limit allows running this goroutine. 65 // Once exitsyscall has returned, it is safe to do things like call the memory 66 // allocator or invoke the Go callback function p.GoF. runtime.cgocallbackg 67 // first defers a function to unwind m->g0.sched.sp, so that if p.GoF 68 // panics, m->g0.sched.sp will be restored to its old value: the m->g0 stack 69 // and the m->curg stack will be unwound in lock step. 70 // Then it calls p.GoF. Finally it pops but does not execute the deferred 71 // function, calls runtime.entersyscall, and returns to runtime.cgocallback. 72 // 73 // After it regains control, runtime.cgocallback switches back to 74 // m->g0's stack (the pointer is still in m->g0.sched.sp), restores the old 75 // m->g0.sched.sp value from the stack, and returns to _cgoexp_GoF. 76 // 77 // _cgoexp_GoF immediately returns to crosscall2, which restores the 78 // callee-save registers for gcc and returns to GoF, which returns to f. 79 80 package runtime 81 82 import "unsafe" 83 84 // Call from Go to C. 85 //go:nosplit 86 func cgocall(fn, arg unsafe.Pointer) { 87 cgocall_errno(fn, arg) 88 } 89 90 //go:nosplit 91 func cgocall_errno(fn, arg unsafe.Pointer) int32 { 92 if !iscgo && GOOS != "solaris" && GOOS != "windows" { 93 throw("cgocall unavailable") 94 } 95 96 if fn == nil { 97 throw("cgocall nil") 98 } 99 100 if raceenabled { 101 racereleasemerge(unsafe.Pointer(&racecgosync)) 102 } 103 104 /* 105 * Lock g to m to ensure we stay on the same stack if we do a 106 * cgo callback. Add entry to defer stack in case of panic. 107 */ 108 lockOSThread() 109 mp := getg().m 110 mp.ncgocall++ 111 mp.ncgo++ 112 defer endcgo(mp) 113 114 /* 115 * Announce we are entering a system call 116 * so that the scheduler knows to create another 117 * M to run goroutines while we are in the 118 * foreign code. 119 * 120 * The call to asmcgocall is guaranteed not to 121 * split the stack and does not allocate memory, 122 * so it is safe to call while "in a system call", outside 123 * the $GOMAXPROCS accounting. 124 */ 125 entersyscall(0) 126 errno := asmcgocall_errno(fn, arg) 127 exitsyscall(0) 128 129 return errno 130 } 131 132 //go:nosplit 133 func endcgo(mp *m) { 134 mp.ncgo-- 135 if mp.ncgo == 0 { 136 // We are going back to Go and are not in a recursive 137 // call. Let the GC collect any memory allocated via 138 // _cgo_allocate that is no longer referenced. 139 mp.cgomal = nil 140 } 141 142 if raceenabled { 143 raceacquire(unsafe.Pointer(&racecgosync)) 144 } 145 146 unlockOSThread() // invalidates mp 147 } 148 149 // Helper functions for cgo code. 150 151 func cmalloc(n uintptr) unsafe.Pointer { 152 var args struct { 153 n uint64 154 ret unsafe.Pointer 155 } 156 args.n = uint64(n) 157 cgocall(_cgo_malloc, unsafe.Pointer(&args)) 158 if args.ret == nil { 159 throw("C malloc failed") 160 } 161 return args.ret 162 } 163 164 func cfree(p unsafe.Pointer) { 165 cgocall(_cgo_free, p) 166 } 167 168 // Call from C back to Go. 169 //go:nosplit 170 func cgocallbackg() { 171 gp := getg() 172 if gp != gp.m.curg { 173 println("runtime: bad g in cgocallback") 174 exit(2) 175 } 176 177 // entersyscall saves the caller's SP to allow the GC to trace the Go 178 // stack. However, since we're returning to an earlier stack frame and 179 // need to pair with the entersyscall() call made by cgocall, we must 180 // save syscall* and let reentersyscall restore them. 181 savedsp := unsafe.Pointer(gp.syscallsp) 182 savedpc := gp.syscallpc 183 exitsyscall(0) // coming out of cgo call 184 cgocallbackg1() 185 // going back to cgo call 186 reentersyscall(savedpc, uintptr(savedsp)) 187 } 188 189 func cgocallbackg1() { 190 gp := getg() 191 if gp.m.needextram { 192 gp.m.needextram = false 193 systemstack(newextram) 194 } 195 196 if gp.m.ncgo == 0 { 197 // The C call to Go came from a thread not currently running 198 // any Go. In the case of -buildmode=c-archive or c-shared, 199 // this call may be coming in before package initialization 200 // is complete. Wait until it is. 201 <-main_init_done 202 } 203 204 // Add entry to defer stack in case of panic. 205 restore := true 206 defer unwindm(&restore) 207 208 if raceenabled { 209 raceacquire(unsafe.Pointer(&racecgosync)) 210 } 211 212 type args struct { 213 fn *funcval 214 arg unsafe.Pointer 215 argsize uintptr 216 } 217 var cb *args 218 219 // Location of callback arguments depends on stack frame layout 220 // and size of stack frame of cgocallback_gofunc. 221 sp := gp.m.g0.sched.sp 222 switch GOARCH { 223 default: 224 throw("cgocallbackg is unimplemented on arch") 225 case "arm": 226 // On arm, stack frame is two words and there's a saved LR between 227 // SP and the stack frame and between the stack frame and the arguments. 228 cb = (*args)(unsafe.Pointer(sp + 4*ptrSize)) 229 case "arm64": 230 // On arm64, stack frame is four words and there's a saved LR between 231 // SP and the stack frame and between the stack frame and the arguments. 232 cb = (*args)(unsafe.Pointer(sp + 5*ptrSize)) 233 case "amd64": 234 // On amd64, stack frame is one word, plus caller PC. 235 if framepointer_enabled { 236 // In this case, there's also saved BP. 237 cb = (*args)(unsafe.Pointer(sp + 3*ptrSize)) 238 break 239 } 240 cb = (*args)(unsafe.Pointer(sp + 2*ptrSize)) 241 case "386": 242 // On 386, stack frame is three words, plus caller PC. 243 cb = (*args)(unsafe.Pointer(sp + 4*ptrSize)) 244 case "ppc64", "ppc64le": 245 // On ppc64, stack frame is two words and there's a 246 // saved LR between SP and the stack frame and between 247 // the stack frame and the arguments. 248 cb = (*args)(unsafe.Pointer(sp + 4*ptrSize)) 249 } 250 251 // Invoke callback. 252 // NOTE(rsc): passing nil for argtype means that the copying of the 253 // results back into cb.arg happens without any corresponding write barriers. 254 // For cgo, cb.arg points into a C stack frame and therefore doesn't 255 // hold any pointers that the GC can find anyway - the write barrier 256 // would be a no-op. 257 reflectcall(nil, unsafe.Pointer(cb.fn), unsafe.Pointer(cb.arg), uint32(cb.argsize), 0) 258 259 if raceenabled { 260 racereleasemerge(unsafe.Pointer(&racecgosync)) 261 } 262 263 // Do not unwind m->g0->sched.sp. 264 // Our caller, cgocallback, will do that. 265 restore = false 266 } 267 268 func unwindm(restore *bool) { 269 if !*restore { 270 return 271 } 272 // Restore sp saved by cgocallback during 273 // unwind of g's stack (see comment at top of file). 274 mp := acquirem() 275 sched := &mp.g0.sched 276 switch GOARCH { 277 default: 278 throw("unwindm not implemented") 279 case "386", "amd64": 280 sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp)) 281 case "arm": 282 sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 4)) 283 case "arm64": 284 sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16)) 285 case "ppc64", "ppc64le": 286 sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 8)) 287 } 288 releasem(mp) 289 } 290 291 // called from assembly 292 func badcgocallback() { 293 throw("misaligned stack in cgocallback") 294 } 295 296 // called from (incomplete) assembly 297 func cgounimpl() { 298 throw("cgo not implemented") 299 } 300 301 var racecgosync uint64 // represents possible synchronization in C code