Write Solana on-chain programs in Zig.
This branch (solana-zig-fork-0.16) supports two build paths so one
SDK serves both audiences:
| Path | Compiler | Linker | CU | Install size |
|---|---|---|---|---|
Primary — buildProgram |
solana-zig fork (Zig 0.16-dev) | built-in lld |
best (matches solana-zig baseline) |
~200 MB |
Fallback — buildProgramElf2sbpf |
stock Zig 0.16 | elf2sbpf |
worse than baseline (no CU optimizer) | stock Zig + ~2 MB |
Which to pick:
- have solana-zig fork installed → use
buildProgram(fork path) — one call, direct.so, optimal CU. - only have stock Zig → use
buildProgramElf2sbpf— slightly more CU, zero extra dependencies beyond theelf2sbpfbinary.
./scripts/bootstrap.sh
zig build test --summary all
./program-test/test.shbootstrap.sh detects both toolchains and prints the environment variables
to export:
export SOLANA_ZIG_BIN=/path/to/solana-zig-bootstrap/out-smoke/host/bin/zig
export ELF2SBPF_BIN=/path/to/elf2sbpf
const std = @import("std");
const solana = @import("solana_program_sdk");
pub fn build(b: *std.Build) void {
_ = solana.buildProgram(b, .{
.name = "my_program",
.root_source_file = b.path("src/main.zig"),
.optimize = .ReleaseFast,
});
}Run with the fork Zig:
"$SOLANA_ZIG_BIN" buildOne step. The SDK sets sbf_target, installs the bpf.ld linker script, and
writes zig-out/lib/my_program.so.
const std = @import("std");
const solana = @import("solana_program_sdk");
pub fn build(b: *std.Build) void {
const target = b.resolveTargetQuery(solana.bpf_target);
const optimize = .ReleaseFast;
const elf2sbpf_bin = b.option(
[]const u8,
"elf2sbpf-bin",
"Path to the elf2sbpf executable",
);
_ = solana.buildProgramElf2sbpf(b, .{
.name = "my_program",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.elf2sbpf_bin = elf2sbpf_bin,
});
}Run with stock Zig:
zig build # elf2sbpf auto-resolved from ELF2SBPF_BIN / .tools / PATHFor CU-optimal Zig programs, use the solana-zig fork path (primary)
instead — the elf2sbpf fallback path is for users who can't install
the fork. A --peephole rewriter existed in earlier elf2sbpf
versions but was rolled back 2026-04-19 (miscompile on escrow-class
programs).
zig version
# -> 0.16.xClone & build:
git clone -b solana-1.52-zig0.16 \
https://github.com/DaviRain-Su/solana-zig-bootstrap \
../solana-zig-bootstrap
cd ../solana-zig-bootstrap
git submodule update --init --recursive
./build native-macos-none baseline # or native-linux-musl baselineWhen done:
export SOLANA_ZIG_BIN=$(pwd)/out-smoke/host/bin/zigbootstrap.sh in this repo also auto-detects a sibling ../solana-zig-bootstrap
checkout.
sbf_target—.cpu_arch = .sbf,.os_tag = .solana,.cpu_model = v2. Only recognized by the fork Zig.bpf_target—.cpu_arch = .bpfel,.os_tag = .freestanding,.cpu_model = v2. Works on stock Zig (kernel-BPF-flavored output, then run throughelf2sbpf).
Stock Zig is fine:
zig build test --summary all./program-test/test.shSee program-test/test.sh for CLI options.
main— original upstream solana-zig based SDK (legacy reference).dev/elf2sbpf-from-4589040— stock Zig + elf2sbpf only (simpler, narrower scope).solana-zig-fork-0.16(this branch) — dual path, recommended default.