Summary
Programs compile to sBPF via LLVM and run in a sandboxed VM with a 1.4M CU budget per transaction. The runtime caches up to 512 compiled programs, provides syscalls for logging, CPI, crypto, and memory, and delays new deployments by 1 slot.
Compilation
Solana uses LLVM to compile programs into ELF binaries containing Solana Bytecode Format (sBPF). The ELF binary is stored on-chain in an executable account.
sBPF is Solana's custom variant of eBPF bytecode, tailored for the Solana runtime. It is not standard eBPF and has Solana-specific modifications.
Write programs
Solana programs are primarily written in Rust using one of two approaches:
Anchor
A framework that uses Rust macros to reduce boilerplate. Recommended for most developers.
Native Rust
Direct Rust without frameworks. Offers full control but requires more manual implementation.
Program execution model
When a transaction is processed, the runtime executes each instruction
sequentially through
process_message().
For each instruction, the runtime:
-
Prepares the instruction context. Calls
prepare_next_top_level_instruction()to map the instruction's account indices, set signer and writable flags, and configure theTransactionContext. -
Checks precompiles. If the program is a precompile, the runtime calls
process_precompile(), which still pushes and pops a stack frame (viapush()andpop()) but bypasses the sBPF VM and program cache lookup, executing native code directly. -
Pushes a stack frame. (Steps 3-6 happen inside
InvokeContext::process_instruction()andprocess_executable_chain(), called fromprocess_message().) Callspush()onInvokeContext, which increments the instruction stack height and enforces the reentrancy rule: a program may only re-enter itself if the immediate caller (the program at the current top of the instruction stack) is the same program. Deep self-recursion (A -> A -> A) is allowed, subject to stack depth limits. Other reentrancy patterns (e.g., A calls B calls A) returnInstructionError::ReentrancyNotAllowed. -
Resolves the program. The runtime calls
process_executable_chain()which determines the loader. If the program account's owner is the native loader, the program is a builtin and its entrypoint function is looked up directly from theProgramCacheForTxBatch. If the owner is one of the BPF loaders (bpf_loader_deprecated,bpf_loader,bpf_loader_upgradeable, orloader_v4), the loader's own builtin entrypoint is invoked instead. -
Executes the BPF program. For BPF programs, the loader entrypoint looks up the compiled executable from the program cache. The
execute()function then:- Serializes account data into a flat parameter buffer
- Creates the sBPF VM with stack, heap, and memory regions
- Runs the compiled code, consuming compute units during execution. Returns
ComputationalBudgetExceededif the budget is exceeded. - Deserializes account data from the buffer back into account state
-
Pops the stack frame. Calls
pop()which verifies that the instruction did not violate the runtime's accounting rules (lamport balances are balanced, readonly accounts were not modified, account data sizes are within limits). -
Accumulates compute units. The compute units consumed by the instruction are added to the transaction total via
saturating_add.
Program cache
The runtime maintains a global
ProgramCache
that stores verified and compiled programs. It is fork-graph aware and handles
deployment visibility rules, eviction, and epoch boundary recompilation.
Cache entry types
Every cached program has a
ProgramCacheEntryType
that determines its runtime behavior:
| Type | Description |
|---|---|
Loaded | Verified and compiled program, ready for execution. |
Builtin | Native program compiled into the validator binary (System, Stake, Vote, etc.). Not stored on-chain. |
Unloaded | Previously verified program whose compiled executable was evicted from memory to free space. Still tracks usage statistics. Can be reloaded without re-verification. |
FailedVerification | Tombstone for programs that did not pass the sBPF verifier under the current feature set. May become Loaded if feature activations change the verification rules. |
Closed | Tombstone for programs that were explicitly closed or never deployed. Also used for accounts (such as buffer accounts) that belong to a loader but do not contain executable code. |
DelayVisibility | Synthetic tombstone returned by ProgramCacheForTxBatch::find() when a Loaded entry exists but is not yet effective (its effective_slot is in the future). Never stored directly in the cache. |
Visibility delay
Newly deployed or upgraded programs are not effective immediately. The
DELAY_VISIBILITY_SLOT_OFFSET
constant is 1, meaning a program deployed in slot N becomes effective in slot
N+1. During the deployment slot, any attempt to invoke the new version returns
DelayVisibility, causing the runtime to report "Program is not deployed."
Eviction policy
The cache holds up to
MAX_LOADED_ENTRY_COUNT
(512) compiled program entries. When the limit is reached, the least-used
programs are evicted to Unloaded state. Usage is tracked by
tx_usage_counter
(incremented each time a transaction references the program) and
latest_access_slot.
Epoch boundary recompilation
If a feature activation changes the
ProgramRuntimeEnvironments
at an epoch boundary, all cached programs are
recompiled
against the new environment.
Return data
Programs can set return data via the sol_set_return_data syscall. The data is
stored in a transaction-level
TransactionReturnData
struct that holds the data bytes and the program_id of the program whose
instruction called the syscall. The maximum size is 1,024 bytes
(MAX_RETURN_DATA).
Is this page helpful?