Program Execution

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:

Program execution model

When a transaction is processed, the runtime executes each instruction sequentially through process_message(). For each instruction, the runtime:

  1. Prepares the instruction context. Calls prepare_next_top_level_instruction() to map the instruction's account indices, set signer and writable flags, and configure the TransactionContext.

  2. Checks precompiles. If the program is a precompile, the runtime calls process_precompile(), which still pushes and pops a stack frame (via push() and pop()) but bypasses the sBPF VM and program cache lookup, executing native code directly.

  3. Pushes a stack frame. (Steps 3-6 happen inside InvokeContext::process_instruction() and process_executable_chain(), called from process_message().) Calls push() on InvokeContext, 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) return InstructionError::ReentrancyNotAllowed.

  4. 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 the ProgramCacheForTxBatch. If the owner is one of the BPF loaders (bpf_loader_deprecated, bpf_loader, bpf_loader_upgradeable, or loader_v4), the loader's own builtin entrypoint is invoked instead.

  5. 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 ComputationalBudgetExceeded if the budget is exceeded.
    • Deserializes account data from the buffer back into account state
  6. 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).

  7. 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:

TypeDescription
LoadedVerified and compiled program, ready for execution.
BuiltinNative program compiled into the validator binary (System, Stake, Vote, etc.). Not stored on-chain.
UnloadedPreviously verified program whose compiled executable was evicted from memory to free space. Still tracks usage statistics. Can be reloaded without re-verification.
FailedVerificationTombstone for programs that did not pass the sBPF verifier under the current feature set. May become Loaded if feature activations change the verification rules.
ClosedTombstone 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.
DelayVisibilitySynthetic 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?

Table of Contents

Edit Page

Managed by

© 2026 Solana Foundation.
All rights reserved.
Get connected