Program Example
Now let's see how registers (r0-r10), memory regions, and instructions work together in an actual program. We'll start with the simplest possible sBPF program to understand the fundamental execution flow.
NoOp-program
A "NoOp" (No Operation) program is perfect for learning because it demonstrates the essential program structure without any complexity:
How programs receive input (in register r1)
How they return results (in register r0)
The basic entry/exit flow every sBPF program follows
How Rust compiles to sBPF assembly
Even though it does "nothing," it shows you the foundation that every Solana program builds on.
Now that we know the basic sBPF operations, let’s see what they look like in a real (even if tiny) program.
Pinocchio NoOp
Below is a high-performance "noop" written with Pinocchio. All it does is return success:
#![no_std]
use pinocchio::{entrypoint::InstructionContext, lazy_program_entrypoint, no_allocator, nostd_panic_handler, ProgramResult};
lazy_program_entrypoint!(process_instruction);
nostd_panic_handler!();
no_allocator!();
fn process_instruction(
_context: InstructionContext, // wrapper around the input buffer
) -> ProgramResult {
Ok(())
}If we build this code with cargo build-sbf --dump, we will get an ELF dump that gives us information about our binary in the /target/deploy/ directory.
We will then want to look for the .text section — the part of our binary where executable code is stored.
Disassembly of section .text
0000000000000120 <entrypoint>
120 b7 00 00 00 00 00 00 00 mov64 r0, 0x0
128 95 00 00 00 00 00 00 00 exitLet's break down what these hex bytes actually mean using the instruction format we learned:
This is the first instruction: 120 b7 00 00 00 00 00 00 00
Address:
0x120(within the text region starting at 0x100000000)Opcode:
0xb7=mov64with immediate valuedst:
0x00= registerr0src:
0x00= unused (immediate operation)offset:
0x0000= unusedimm:
0x00000000= immediate value 0
And this is the second instruction: 128 95 00 00 00 00 00 00 00
Address:
0x128(8 bytes after the first instruction)Opcode:
0x95= exit/returnAll other fields:
0x00= unused for exit
Assembly NoOp
If we were to disassemble the binary to turn it back into compilable sBPF Assembly, the code would look like this:
.globl entrypoint
entrypoint:
mov64 r0, 0x00 // r0 <- success
exit // finish, return r0Let's break down the code:
.globl entrypoint: This is an assembler directive that tells the linker to make the entrypoint symbol globally visible. The Solana runtime looks for this symbol to know where to start executing your program.entrypoint: This is a label that marks the memory address where program execution begins. When the runtime loads your program, it jumps to this address.mov64 r0, 0x00: Move the immediate value 0 into register r0. Since r0 is the return register, this sets up a success return code.exit: Terminate program execution and return the value in r0 to the runtime.
This is an extremely small program, with just 2 instructions consuming only 2 compute units (CUs) to execute that maps perfectly to our Rust code:
We defined an entrypoint function
We return Ok(()) which evaluates to 0
The compiler generated the appropriate
mov64andexitinstructions
Optimized Assembly NoOp
However, we can optimize this further. Since the Solana runtime initializes r0 to 0 by default, we can eliminate the redundant mov64 instruction:
.globl entrypoint
entrypoint:
exitThis optimized version:
Costs only 1 compute unit (50% reduction!)
Produces identical results (
r0still contains 0)
This optimization is possible because we know the initial register states — something the Rust compiler doesn't always take advantage of. Understanding sBPF assembly lets you identify and eliminate such inefficiencies in performance-critical code.