Getting started
The build pipeline
Follow C source through Clang, C2Go LTO, binding generation, and the Go toolchain.Maintained byC2Go contributorsC2Go separates compilation from package generation. Understanding that split makes build failures much easier to diagnose.
Stage 1: C frontend and LLVM bitcode
c2go-clang parses C with the C2Go language extensions enabled. The target triple controls both machine code and C data-model details—for example, long is 8 bytes on Linux and Darwin but 4 bytes on 64-bit Windows.
A normal -fc2go -c compile stops at pre-link LLVM bitcode and names the result .o for build-system compatibility. When Plan 9 assembly or manifest output is requested directly, the driver takes those same per-source bitcode modules and invokes the colocated c2go-lto tool immediately.
Stage 2: C2Go LTO
c2go-lto links one or more bitcode modules and runs the C2Go lowering pipeline. This is where whole-program operations such as cross-translation-unit inlining, escape auditing, Go-stack handling, GC statepoints, write barriers, ABI lowering, and final Plan 9 emission are coordinated.
Even a single-source driver invocation uses this internal path when emit flags are present:
cc1 → LLVM bitcode → c2go-lto → output.s + output.json
-O0 and -O2 are both supported. -O0 keeps the simpler, primarily GoABI0 stack-argument path and skips the cross-TU inlining, statepoint GC, and leaf internal-ABI optimizations enabled at -O2; return ABI behavior remains consistent across optimization levels.
The ordinary driver invokes its internal escape audit in nonfatal mode and suppresses its report in this release. That internal pass is not the release safety gate. Build all translation units as audit bitcode and invoke standalone c2go-lto as described in Stack escape safety.
Stage 3: Manifest-guided binding
The assembly alone is not enough. The JSON manifest carries information the Go source generator cannot recover reliably from text assembly, including:
- Go import path and exported symbol names;
- target
GOOSandGOARCH; - minimum Go version, validation snapshot, C2Go ABI epoch, and Go-toolchain contract epoch;
- record layouts, fields, and GC pointer masks;
- Go linknames and multi-value returns;
- native imports, libraries, callbacks, and callouts.
c2go-bind validates agreement across every input before writing files. Conflicting targets, contract epochs, symbols, types, or callback metadata fail closed. The meaning of each version field is covered in Versions and compatibility.
Stage 4: Ordinary Go build
The generated directory is an ordinary Go package. It contains Go declarations, build-tagged or target-specific assembly, runtime anchors, and any required dynamic-import glue. The Go toolchain assembles and links those files together with the pinned c2go-libc module.
There is no C compiler involved at application startup.
Keep these values coordinated
| Value | Why it must match |
|---|---|
-fc2go-package |
The single source of the final Go import path; also identifies same-package linknames and is carried by the manifest |
| assembly and manifest | They describe the same compiled unit |
| target triple and Go build target | Plan 9 assembly is OS/architecture-specific |
| compiler, binder, and libc release | Manifest and runtime ABI are versioned together |
| Go toolchain version | Stack-frame and ABI contracts are checked during generation |
Useful inspection points
If a build fails, preserve the .s and .json pair. Check the manifest package path, target, and ABI version before debugging generated Go. Use c2go-bind --dry-run to inspect Go output without changing an output directory, and c2go-lto --c2go-print-stats when diagnosing the compiler pipeline directly.
c2go-bind derives the Go package name from the final segment of manifest pkgpath. Pass --pkgname only when they genuinely differ—for example, import path github.com/c2gohq/c2go_libc with source clause package libc.
Next, export a C API to Go. When moving beyond manual commands, use the Makefile and CMake integration guide.