cgo
Purpose and Scope
cgo is the part of the Go toolchain that lets a Go package call C code. In normal Go source, a file opts in by importing the pseudo-package C; code in that file can then refer to C types, variables, functions, and declarations exposed through the special import. This page explains cgo as a toolchain component rather than as a general foreign-function-interface tutorial: it focuses on how the command is entered, how source comments and directives become compiler and linker inputs, and why cross-platform builds require explicit C compiler configuration.
Sources: src/cmd/cgo/doc.go, src/cmd/cgo/main.go
The design fits the broader Go command model: developers should usually describe build-relevant information in source files and package metadata, not external makefiles. cgo keeps that convention by using Go syntax plus comments immediately before import "C". Those comments form a C preamble that is treated as a header for compiling the C parts of the package. Because the preamble can include declarations, definitions, and #include lines, a Go package can present a small C boundary while still being built by the go command.
Sources: src/cmd/cgo/doc.go
Relevant Source Files
src/cmd/cgo/doc.go— User-facing command documentation for cgo, including theimport "C"pattern, preambles,#cgodirectives, pkg-config integration, environment variables, and security restrictions on flags.src/cmd/cgo/main.go— Main implementation entry point and internal model for packages, files, references toC.xxx, calls, exported functions, compiler options, linker flags, and generated output bookkeeping.misc/ios/README— Platform-specific operational guidance showing how cgo interacts with target C compilers,CGO_ENABLED,CC,CC_FOR_TARGET,GOOS,GOARCH, and iOS execution wrappers.
Core Model
A cgo-enabled Go file imports the pseudo-package C. If a comment immediately precedes that import, the comment is the preamble. The preamble is not ordinary documentation: it is consumed by cgo as C input, so it may contain #include directives, declarations, definitions, and #cgo build directives. The documentation calls out that names declared in the preamble can be referenced from Go as if they were in package C, including lower-case C names. Static variables are an exception, while static functions are permitted.
Sources: src/cmd/cgo/doc.go
Inside the implementation, cgo records this relationship explicitly. A Package value collects the Go package name and path, pointer and integer sizes, GCC-derived options, linker flags, input Go files, generated C files, the combined preamble for _cgo_export.h, and maps for special directives such as nocallback and noescape. A File value tracks the parsed Go AST, comment groups, file-level package name, C preamble, every C.xxx reference, every call to C.xxx, exported functions, and editing state used while rewriting source.
Sources: src/cmd/cgo/main.go
The implementation vocabulary is useful when debugging cgo behavior. A Ref is an expression of the form C.xxx; a Call is a call expression involving a C symbol and records whether it was deferred or already handled; a Name captures the Go spelling, mangled generated name, C spelling, kind, constant value, and type information for a referenced C entity. That means cgo is not simply passing text to a C compiler. It parses Go, identifies the cross-language boundary, classifies C names, and generates Go and C artifacts that the rest of the toolchain can compile and link.
Sources: src/cmd/cgo/main.go
Command and Directive Reference
The principal source-level switch is import "C". The principal configuration mechanism is the #cgo directive embedded in the preamble. The documented directive variables are CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS. Multiple directives concatenate their values, and a directive may include build constraints so that a flag applies only on selected operating systems, architectures, or other constraint combinations. Package authors should prefer these directives for package-specific flags because they travel with the source and support reproducible builds in unmodified environments.
Sources: src/cmd/cgo/doc.go
For dependency discovery, #cgo pkg-config: asks pkg-config for compiler and linker flags for named packages. The default pkg-config tool can be changed with the PKG_CONFIG environment variable. At build time, the Go command also adds CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS, and CGO_LDFLAGS from the environment. The documentation deliberately distinguishes these environment variables from source directives: environment flags are not subject to the same security limitations and are better suited to local build environment customization than package-distributed requirements.
Sources: src/cmd/cgo/doc.go
A compact reference for common inputs is: import "C" enables cgo in a file; the immediately preceding comment is the preamble; #cgo CFLAGS: and #cgo CPPFLAGS: affect C compilation; #cgo CXXFLAGS: affects C++ compilation along with CPP flags; #cgo FFLAGS: affects Fortran compilation along with CPP flags; #cgo LDFLAGS: contributes link flags across packages in the final program; #cgo pkg-config: invokes pkg-config; PKG_CONFIG selects the pkg-config executable; and the CGO_*_ALLOW or CGO_*_DISALLOW variables tune which flag arguments are accepted from directives.
Sources: src/cmd/cgo/doc.go
C Compiler Integration and Safety Rules
cgo exists at a boundary where Go source, C headers, generated code, and the platform C compiler all participate in one build. The documentation states that CPP and C flags from all cgo directives in a package are concatenated for C files in that package. C++ files combine CPP and CXX flags, Fortran files combine CPP and Fortran flags, and linker flags from any package in the program are accumulated for link time. This accumulation model is important because a library package can contribute link requirements that are only satisfied when the final executable is built.
Sources: src/cmd/cgo/doc.go
The flag handling is intentionally constrained. For security reasons, only a limited set of flags is accepted in #cgo directives by default, notably forms such as -D, -U, -I, and -l. Additional flags can be allowed with a matching regular expression in variables such as CGO_CFLAGS_ALLOW, and otherwise allowed flags can be rejected with variables such as CGO_CFLAGS_DISALLOW. The documented rule is that these regular expressions must match a full argument, which prevents a partial pattern from accidentally permitting more than intended.
Sources: src/cmd/cgo/doc.go
Character-level restrictions add another guardrail. The cgo documentation says only a limited set of characters is permitted in directive arguments, with forbidden characters producing a malformed #cgo argument error. This matters because cgo comments become compiler or linker command-line inputs. The restrictions are part of the contract between source-distributed packages and users who run go build: package-local directives can describe ordinary compiler requirements, but they should not become an arbitrary shell-like command channel.
Sources: src/cmd/cgo/doc.go
Cross-Platform Implications
Cross-compilation with cgo is different from cross-compiling pure Go code because a target C compiler must also exist and be selected correctly. The iOS README shows this explicitly. To run standard-library tests on the iOS emulator, the example sets GOOS=ios, GOARCH=amd64, CGO_ENABLED=1, and CC_FOR_TARGET to the repository's iOS clang wrapper before running all.bash. That combination tells the Go build which platform is targeted and tells cgo which C compiler wrapper can produce target-compatible objects.
Sources: misc/ios/README
The same README distinguishes toolchain-build-time and command-line compiler selection. If CC_FOR_TARGET was set when building the toolchain with make.bash or all.bash, later builds do not need to repeat CC. If it was not, a build can set CC on the command line, for example by pointing it at $(go env GOROOT)/misc/ios/clangwrap.sh. The distinction matters because cgo is invoked by the Go build, but it ultimately relies on an external compiler whose target must match the Go target.
Sources: misc/ios/README
Running programs and tests on iOS adds another layer after compilation. The README instructs users to put $GOROOT/bin on PATH so the go_ios_$GOARCH_exec wrapper can be found, then run commands such as GOOS=ios GOARCH=amd64 CGO_ENABLED=1 go test archive/tar. The wrapper uses GOARCH to choose emulator or device behavior. On a physical device, additional signing and provisioning environment variables are required, including GOIOS_DEV_ID, GOIOS_TEAM_ID, and GOIOS_APP_ID. This is a concrete example of cgo's platform cost: a successful build may still depend on platform-specific execution tooling.
Sources: misc/ios/README
Execution Flow
A practical cgo build starts when the go command loads a package and finds files that import C. cgo parses those Go files, reads the preamble comments, identifies C.xxx references and calls, gathers package-wide compiler and linker directives, and prepares generated Go and C outputs. The data structures in src/cmd/cgo/main.go show the phases as state: parsed ASTs, comments, names, references, exported functions, GCC options, linker flags, generated file lists, and preamble text are all retained so later generation steps can make consistent code across all files in the package.
Sources: src/cmd/cgo/main.go
From the developer's perspective, the result should still feel like a normal Go build command. The source file imports packages, including C, and the Go command arranges compilation and linking. The difference is that cgo bridges to external C tooling and therefore needs enough information to compile non-Go sources. When a build fails, the useful debugging questions follow the model above: did the Go file import C with the intended preamble, were the right #cgo directives selected by build constraints, did pkg-config return the expected flags, and is the selected C compiler appropriate for the target platform?
Sources: src/cmd/cgo/doc.go, src/cmd/cgo/main.go, misc/ios/README
Next Steps
When adding cgo to a package, begin with the smallest possible preamble and put package-specific compiler or linker requirements in #cgo directives rather than relying on local environment variables. Use pkg-config when the dependency already exposes suitable metadata, and reserve CGO_* environment variables for local toolchain customization. For cross-platform work, test the pure Go target variables and the C compiler selection together; GOOS, GOARCH, CGO_ENABLED, CC, and CC_FOR_TARGET form one configuration surface, not independent switches.
Sources: src/cmd/cgo/doc.go, misc/ios/README
Related pages that usually come next are go-command for how the Go command dispatches builds, build-and-install for normal package compilation behavior, and toolchain-command-reference for other commands shipped with the distribution. If your immediate task is a platform port, read the platform README first and treat its compiler-wrapper and execution-wrapper instructions as part of the cgo build contract.