Download and analyze Solana BPF programs from mainnet.
The tool will automatically create a .env file with the default mainnet RPC endpoint on first run:
RPC_ENDPOINT=https://api.mainnet-beta.solana.comTo use a custom RPC endpoint, edit the .env file before running sync.
cargo build --releaseDownload all programs from mainnet:
program-sync syncThis will:
- Fetch all BPF programs from the specified loaders (default: v1, v2, v3)
- Store metadata in
solana_programs.db - Download program binaries to
programs/directory - Track deployment slots for incremental updates
On subsequent runs, only new or updated programs are downloaded.
Query sBPF instructions across all downloaded programs:
# Count all call instructions
program-sync analyze --opcode call --count
# Show distribution of src registers for call instructions
program-sync analyze --opcode call --agg src
# Count sub32 instructions where imm=1
program-sync analyze --opcode sub32 --count imm=1
# Count add64 with multiple filters
program-sync analyze --opcode add64 --count src=1,dst=2
# Count instructions with src=2 across ALL opcodes
program-sync analyze --opcode all --count src=2Fetch programs from Solana RPC and save their binaries locally.
program-sync sync [OPTIONS]Options:
--loader <VERSION>- Loader version (1-4), repeatable--rpc-url <URL>- Custom RPC endpoint--verbose, -v- Log skipped/errored programs--help, -h- Show help
Loader Versions:
1- BPFLoader v12- BPFLoader v23- BPFLoaderUpgradeable (most common)4- LoaderV4 (experimental)
Examples:
# Sync only BPFLoaderUpgradeable programs
program-sync sync --loader 3
# Sync multiple loaders
program-sync sync --loader 2 --loader 3
# Use custom RPC
program-sync sync --rpc-url https://my-rpc.com --verboseAnalyze sBPF instructions across all downloaded programs.
program-sync analyze --opcode <OPCODE> [OPTIONS]Options:
--opcode <OPCODE>- sBPF opcode to analyze, orallfor all opcodes (required)--agg <FIELD>- Aggregate and show distribution by field (src,dst,imm,off)--count [FILTERS]- Count matching instructions (optionally with filters)--dir <PATH>- Program directory (default:programs)--help, -h- Show help
Examples:
# Distribution of destination registers for add64
program-sync analyze --opcode add64 --agg dst
# Count jeq instructions with specific offset
program-sync analyze --opcode jeq --count off=10
# Show distribution of imm values for mov64
program-sync analyze --opcode mov64 --agg imm
# Count instructions with src=2 across ALL opcodes
program-sync analyze --opcode all --count src=2
# Distribution of src registers across ALL opcodes
program-sync analyze --opcode all --agg srcDetect programs that read a register before writing to it using DFG analysis.
program-sync dfg --uninit-reg <N> [OPTIONS]Options:
--uninit-reg <N>- Register to check (0-10), required--dir <PATH>- Program directory (default:programs)--disasm- Show disassembled instruction at each flagged location--entry-only- Only show reads from program entrypoint (filters out internal function parameters)--help, -h- Show help
How it works:
- Builds a data-flow graph for each program using
solana-sbpf - Finds reads where the register value comes from a PhiNode (undefined at that point)
- Filters out EXIT instructions (false positives from liveness analysis)
- With
--entry-only, only reports reads sourced from the program's actual entrypoint
Examples:
# Find all programs that read r2 before writing
program-sync dfg --uninit-reg 2
# Show disassembly for flagged instructions
program-sync dfg --uninit-reg 2 --disasm
# Only check reads from actual program entry (excludes internal function params)
program-sync dfg --uninit-reg 2 --entry-only --disasm
# Check a different register
program-sync dfg --uninit-reg 3 --dir test_programsOutput:
DFG Analysis
============================================================
Found 50 .so files to analyze
Checking for uninitialized reads of r2
============================================================
RESULTS
============================================================
Files processed: 50
Files with errors: 0
Found 49 programs with uninitialized reads of r2:
12FybZF6vtVTDBSHFpwn2hNcKaK8ELWZuxJHbELYcPjV (entry=0): 1 location(s) at pc [23]
1252p8FBsE7dMpr3DAjkQtMN6Jfvt3RbP2YJKjW3t8z (entry=0): 2 location(s) at pc [66, 10]
...
Note: Without --entry-only, many hits are internal functions that expect the register as a parameter (properly set by caller). Use --entry-only to find true uninitialized reads from program start.
SQLite database at solana_programs.db:
CREATE TABLE programs (
pubkey TEXT PRIMARY KEY,
lamports INTEGER NOT NULL,
loader_version INTEGER NOT NULL,
is_closed INTEGER NOT NULL,
last_updated_slot INTEGER NOT NULL,
derived_executable_pubkey TEXT
);Fields:
pubkey- Program account public keyloader_version- Loader version (1-4)is_closed- 1 if program is closed, 0 if activelast_updated_slot- Deployment slot (for incremental updates)derived_executable_pubkey- ProgramData account address (for BPFUpgradeable)
solana_programs.db # SQLite database
programs/
├── <pubkey1>.so # Program binary (ELF)
├── <pubkey2>.so
└── ...
RPC_ENDPOINT- Solana RPC URL
A .env file is automatically created with the default mainnet RPC on first run. To use a custom RPC, edit .env:
# .env
RPC_ENDPOINT=https://your-custom-rpc.comsBPF Instruction Analyzer
============================================================
Found 14975 .so files to analyze
Analyzing opcode: add64
============================================================
ANALYSIS RESULTS
============================================================
Files processed: 14973
Files with errors: 2
Total add64 instructions: 51044156
add64 instruction dst field distribution:
------------------------------------------------------------
DST Count Percentage
------------------------------------------------------------
0 484774 0.95 %
1 24480449 47.96 %
2 12333543 24.16 %
3 3368742 6.60 %
...