Calling Go Functions from C: Implementation Steps and Underlying Mechanism
This article explains how to call Go functions from C within the same process, covering the three‑step implementation, compilation into a shared library, the C invocation code, and a detailed walkthrough of the cgo and Go runtime mechanisms that make cross‑language calls possible.
In this article we explore whether a language can invoke functions implemented in another language within the same process, using C calling Go as a concrete example.
1. C calling Go function example
The implementation consists of three steps:
Define and implement a function in Go.
Compile the Go code into a static or dynamic library.
Call the library from C.
1.1 Go function definition and implementation
package main
//int add(int a, int b);
import "C"
//export add
func add(a, b C.int) C.int {
return a + b
}
func main() {}Key points:
The line import "C" enables cgo, causing go build to invoke gcc.
The comment //int add(int a, int b) is not a Go comment but a C declaration.
//export add makes the function visible to external callers.
Parameters must use the C types provided by cgo (e.g., C.int ).
1.2 Compiling Go code into a library
# go build -buildmode=c-shared -o libadd.dylib main.goThe flag -buildmode=c-shared produces a dynamic library ( .dylib on macOS, .so on Linux); -buildmode=c-archive would produce a static archive.
1.3 C code calling the library
#include
#include "libadd.h"
int main(void) {
int ret = add(2, 3);
printf("C调用Go函数2+3= %d\n", ret);
return 0;
}Compile and link the C program with the generated library:
# gcc main.c -L. -ladd -o mainRunning the executable prints C调用Go函数2+3= 5 , confirming the successful cross‑language call.
2. Underlying mechanism of C calling Go
2.1 cgo compilation tool
Running go tool cgo main.go generates intermediate files such as main.cgo1.go , main.cgo2.c , _cgo_gotypes.go , and the export files _cgo_export.c and _cgo_export.h inside the _obj directory.
2.2 Function entry in _cgo_export.c
int add(int a, int b) {
__SIZE_TYPE__ _cgo_ctxt = _cgo_wait_runtime_init_done();
typedef struct {
int p0;
int p1;
int r0;
} __attribute__((__packed__)) _cgo_argtype;
static _cgo_argtype _cgo_zero;
_cgo_argtype _cgo_a = _cgo_zero;
_cgo_a.p0 = a;
_cgo_a.p1 = b;
crosscall2(_cgoexp_ec46b88da812_add, &_cgo_a, 12, _cgo_ctxt);
return _cgo_a.r0;
}This wrapper ensures the Go runtime is initialized and then forwards the call to crosscall2 , packing the arguments into a struct.
2.3 Go runtime crosscall2
//file:runtime/cgo/asm_amd64.s
TEXT crosscall2(SB),NOSPLIT,$0-0
PUSH_REGS_HOST_TO_ABI0()
ADJSP $0x18
MOVQ CX, 0x0(SP) // fn
MOVQ DX, 0x8(SP) // arg
MOVQ R9, 0x10(SP) // ctxt
CALL runtime·cgocallback(SB)
ADJSP $-0x18
POP_REGS_HOST_TO_ABI0()
RETThe function saves caller registers, prepares the stack, and invokes runtime·cgocallback , which switches from the C thread stack to a Go goroutine stack and locks the OS thread.
2.4 Go stub generated in _cgo_gotypes.go
//go:cgo_export_dynamic add
//go:linkname _cgoexp_ec46b88da812_add _cgoexp_ec46b88da812_add
//go:cgo_export_static _cgoexp_ec46b88da812_add
func _cgoexp_ec46b88da812_add(a *struct{p0 _Ctype_int; p1 _Ctype_int; r0 _Ctype_int}) {
a.r0 = add(a.p0, a.p1)
}This stub converts the packed C arguments back to Go types and calls the user‑defined add function:
//export add
func add(a, b C.int) C.int {
return a + b
}3. Summary
The cross‑language call involves three cooperating parts: the user code (Go function and C caller), the cgo‑generated stub code, and the Go runtime (functions like crosscall2 , cgocallback , and related helpers) that handle register saving, stack switching, and thread locking.
The first entry point is the stub in _cgo_export.c .
The Go runtime then performs ABI conversion, stack switching, and invokes the actual Go function.
After runtime processing, the call returns through the generated stub back to the C side.
Compared with ordinary in‑process function calls, cross‑language calls incur additional overhead due to runtime coordination, but they remain faster than RPC because they avoid kernel‑level protocol handling.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.