| // 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. |
| |
| #include "runtime.h" |
| #include "arch_GOARCH.h" |
| #include "stack.h" |
| #include "cgocall.h" |
| #include "race.h" |
| #include "../../cmd/ld/textflag.h" |
| |
| // 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) locks g to m, 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 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. |
| // |
| // _cgoexp_GoF calls runtime.cgocallback(p.GoF, frame, framesize). |
| // (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.) |
| // |
| // 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.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 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. |
| // |
| // 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. |
| // |
| // _cgoexp_GoF immediately returns to crosscall2, which restores the |
| // callee-save registers for gcc and returns to GoF, which returns to f. |
| |
| void *_cgo_init; /* filled in by dynamic linker when Cgo is available */ |
| static int64 cgosync; /* represents possible synchronization in C code */ |
| |
| static void unwindm(void); |
| |
| // Call from Go to C. |
| |
| static void endcgo(void); |
| static FuncVal endcgoV = { endcgo }; |
| |
| void |
| runtime·cgocall(void (*fn)(void*), void *arg) |
| { |
| Defer d; |
| |
| if(!runtime·iscgo && !Solaris && !Windows) |
| runtime·throw("cgocall unavailable"); |
| |
| if(fn == 0) |
| runtime·throw("cgocall nil"); |
| |
| if(raceenabled) |
| runtime·racereleasemerge(&cgosync); |
| |
| // Create an extra M for callbacks on threads not created by Go on first cgo call. |
| if(runtime·needextram && runtime·cas(&runtime·needextram, 1, 0)) |
| runtime·newextram(); |
| |
| m->ncgocall++; |
| |
| /* |
| * Lock g to m to ensure we stay on the same stack if we do a |
| * cgo callback. Add entry to defer stack in case of panic. |
| */ |
| runtime·lockOSThread(); |
| d.fn = &endcgoV; |
| d.siz = 0; |
| d.link = g->defer; |
| d.argp = NoArgs; |
| d.special = true; |
| g->defer = &d; |
| |
| m->ncgo++; |
| |
| /* |
| * 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 |
| * split the stack and does not allocate memory, |
| * so it is safe to call while "in a system call", outside |
| * the $GOMAXPROCS accounting. |
| */ |
| runtime·entersyscall(); |
| runtime·asmcgocall(fn, arg); |
| runtime·exitsyscall(); |
| |
| if(g->defer != &d || d.fn != &endcgoV) |
| runtime·throw("runtime: bad defer entry in cgocallback"); |
| g->defer = d.link; |
| endcgo(); |
| } |
| |
| static void |
| endcgo(void) |
| { |
| runtime·unlockOSThread(); |
| m->ncgo--; |
| if(m->ncgo == 0) { |
| // We are going back to Go and are not in a recursive |
| // call. Let the GC collect any memory allocated via |
| // _cgo_allocate that is no longer referenced. |
| m->cgomal = nil; |
| } |
| |
| if(raceenabled) |
| runtime·raceacquire(&cgosync); |
| } |
| |
| // Helper functions for cgo code. |
| |
| void (*_cgo_malloc)(void*); |
| void (*_cgo_free)(void*); |
| |
| void* |
| runtime·cmalloc(uintptr n) |
| { |
| struct { |
| uint64 n; |
| void *ret; |
| } a; |
| |
| a.n = n; |
| a.ret = nil; |
| runtime·cgocall(_cgo_malloc, &a); |
| if(a.ret == nil) |
| runtime·throw("runtime: C malloc failed"); |
| return a.ret; |
| } |
| |
| void |
| runtime·cfree(void *p) |
| { |
| runtime·cgocall(_cgo_free, p); |
| } |
| |
| // Call from C back to Go. |
| |
| static FuncVal unwindmf = {unwindm}; |
| |
| typedef struct CallbackArgs CallbackArgs; |
| struct CallbackArgs |
| { |
| FuncVal *fn; |
| void *arg; |
| uintptr argsize; |
| }; |
| |
| // Location of callback arguments depends on stack frame layout |
| // and size of stack frame of cgocallback_gofunc. |
| |
| // 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. |
| #ifdef GOARCH_arm |
| #define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+4*sizeof(void*)) |
| #endif |
| |
| // On amd64, stack frame is one word, plus caller PC. |
| #ifdef GOARCH_amd64 |
| #define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+2*sizeof(void*)) |
| #endif |
| |
| // Unimplemented on amd64p32 |
| #ifdef GOARCH_amd64p32 |
| #define CBARGS (CallbackArgs*)(nil) |
| #endif |
| |
| // On 386, stack frame is three words, plus caller PC. |
| #ifdef GOARCH_386 |
| #define CBARGS (CallbackArgs*)((byte*)m->g0->sched.sp+4*sizeof(void*)) |
| #endif |
| |
| void runtime·cgocallbackg1(void); |
| |
| #pragma textflag NOSPLIT |
| void |
| runtime·cgocallbackg(void) |
| { |
| if(g != m->curg) { |
| runtime·prints("runtime: bad g in cgocallback"); |
| runtime·exit(2); |
| } |
| |
| runtime·exitsyscall(); // coming out of cgo call |
| runtime·cgocallbackg1(); |
| runtime·entersyscall(); // going back to cgo call |
| } |
| |
| void |
| runtime·cgocallbackg1(void) |
| { |
| CallbackArgs *cb; |
| Defer d; |
| |
| if(m->needextram) { |
| m->needextram = 0; |
| runtime·newextram(); |
| } |
| |
| // Add entry to defer stack in case of panic. |
| d.fn = &unwindmf; |
| d.siz = 0; |
| d.link = g->defer; |
| d.argp = NoArgs; |
| d.special = true; |
| g->defer = &d; |
| |
| if(raceenabled) |
| runtime·raceacquire(&cgosync); |
| |
| // Invoke callback. |
| cb = CBARGS; |
| runtime·newstackcall(cb->fn, cb->arg, cb->argsize); |
| |
| if(raceenabled) |
| runtime·racereleasemerge(&cgosync); |
| |
| // Pop defer. |
| // Do not unwind m->g0->sched.sp. |
| // Our caller, cgocallback, will do that. |
| if(g->defer != &d || d.fn != &unwindmf) |
| runtime·throw("runtime: bad defer entry in cgocallback"); |
| g->defer = d.link; |
| } |
| |
| static void |
| unwindm(void) |
| { |
| // Restore sp saved by cgocallback during |
| // unwind of g's stack (see comment at top of file). |
| switch(thechar){ |
| default: |
| runtime·throw("runtime: unwindm not implemented"); |
| case '8': |
| case '6': |
| m->g0->sched.sp = *(uintptr*)m->g0->sched.sp; |
| break; |
| case '5': |
| m->g0->sched.sp = *(uintptr*)((byte*)m->g0->sched.sp + 4); |
| break; |
| } |
| } |
| |
| void |
| runtime·badcgocallback(void) // called from assembly |
| { |
| runtime·throw("runtime: misaligned stack in cgocallback"); |
| } |
| |
| void |
| runtime·cgounimpl(void) // called from (incomplete) assembly |
| { |
| runtime·throw("runtime: cgo not implemented"); |
| } |
| |
| // For cgo-using programs with external linking, |
| // export "main" (defined in assembly) so that libc can handle basic |
| // C runtime startup and call the Go program as if it were |
| // the C main function. |
| #pragma cgo_export_static main |