Assembly
Giới thiệu về Assembly

Giới thiệu về Assembly

Nội dung này đang được dịch và sẽ hiển thị ở đây khi sẵn sàng.

sBPF Registers and Memory Model

When your sBPF program executes, it operates with two primary storage mechanisms: 11 high-speed registers built into the processor and organized memory regions that hold everything else.

Registers

Registers are like 11 numbered storage slots (r0 through r10) built directly into the processor. Each holds a single 64-bit value and provides instant access without delays. When you execute add64 r1, r2, the processor immediately retrieves values from both registers and performs the calculation.

The tradeoff is simple: you only get 11 boxes total, so you need to use them strategically for the values you're actively working with.

sBPF assigns specific roles to each register:

RegisterRoleCallee-saved?Usage Notes
r0Return valueNoFunction results, syscall return values
r1Input buffer pointerNoPoints to 0x400000000 on entry
r2-r5Scratch/argumentsNoHelper function arguments, temporary values
r6-r9General purposeYesMust preserve across calls
r10Frame pointerYes, read-onlyStack base, cannot modify directly

Registers r6 through r9 are "callee-saved," meaning their values persist across function calls. Use these for important data that must survive function invocations.

The remaining registers (r0-r5) get overwritten during syscalls and function calls.

Register r10 is "special" because it serves as your frame pointer, pointing to your stack space base and remaining read-only. You access stack variables using negative offsets like [r10 - 8] and [r10 - 16].

Memory

While registers hold actively used values, memory stores everything else. sBPF organizes memory into fixed regions with identical layouts across all programs:

RegionStart AddressPurposeSize/Notes
Text0x100000000Code and read-only dataProgram binary
Stack0x200000000Local variables32 KiB, grows downward
Heap0x300000000Dynamic allocationVia sol_alloc_free
Input0x400000000Program parametersSerialized arguments
  • The text region contains your executable code and read-only data like string constants. Data defined with .quad directives typically resides here.
  • The stack region houses local variables. With r10 pointing to the stack base, you access locals using negative offsets: [r10 - 16], [r10 - 24], etc. The stack is limited to 32 KiB total.
  • The input region contains your program's parameters. On entry, r1 points to this region (0x400000000), allowing you to read serialized account data and instruction parameters passed to your program.

This fixed layout provides three key benefits: security through memory isolation between programs, determinism with identical addresses across all validators, and performance through compiler optimizations for known locations.

Attempting to access unmapped addresses triggers an AccessViolation and the transaction fails.

Using Registers and Memory

Here's how registers and memory work together in practice:

.globl entrypoint
entrypoint:
    # On entry: r1 points to input data at 0x400000000
    
    ldxdw r0, [r1 + 0]      # Load first 8 bytes from input into r0
    mov64 r2, 42            # Put 42 in register r2
    add64 r0, r2            # Add them: r0 = r0 + r2
    
    stxdw [r10 - 8], r0     # Store result on stack
    mov64 r0, 0             # Return success
    exit

This program demonstrates the typical pattern:

  • Uses register r1 (input pointer) to read from memory
  • Uses registers r0 and r2 for calculations
  • Uses register r10 (frame pointer) to access stack memory
  • Returns the result in register r0

The workflow follows a consistent pattern: load data from memory into registers, perform calculations with registers, then store results back to memory when needed.

Stack Usage

The stack operates with r10 as the frame pointer, pointing to the base of your current stack frame (the highest address). Local variables use negative offsets:

# Store a value on the stack
mov64 r0, 42
stxdw [r10 - 8], r0         ; Store at first stack slot
 
# Load it back
ldxdw r1, [r10 - 8]         ; Load from first stack slot

Stack slots are typically 8 bytes wide, so consecutive variables use offsets like [r10 - 8], [r10 - 16], [r10 - 24], and so on.

Program Entry and Exit

Your program begins execution at the symbol marked with .globl (typically entrypoint). The initial register state is minimal and predictable:

.globl entrypoint
entrypoint:
    # On entry:
    # r1 = 0x400000000 (input buffer pointer)
    # r0, r2-r10 = 0 (all other registers zeroed)
 
    # Your program logic here
 
    mov64 r0, 0     # Success code (0 = SUCCESS)
    exit            # Return to runtime

Exit behavior depends on call depth:

  • At call depth 0, exit terminates the program with r0 as the result code.
  • At deeper call levels, exit acts like a return statement, restoring caller-saved registers (r6-r9) and the caller's frame pointer (r10) before continuing execution at the return address.

The runtime automatically handles setup and teardown through implicit prologue and epilogue code, including initial register state configuration and final return processing.

Nội dung
Xem mã nguồn
Blueshift © 2025Commit: f7a03c2