Skip to Content
rbpfHelpers

Helpers

Relevant source files

A helper is a host function that eBPF code calls with CALL imm. rbpf has two separate helper mechanisms: one for the VM wrappers (interpreter, x86_64 JIT, Cranelift) and one for the AArch64 compiler.

VM wrapper helpers

pub type Helper = fn(u64, u64, u64, u64, u64) -> u64; pub fn register_helper(&mut self, key: u32, function: Helper) -> Result<(), Error>;

Helpers live in a HashMap<u32, Helper>, so any u32 key works. Use the Linux helper numbers if you want kernel-compatible programs. Arguments come from r1–r5 and the return value goes to r0.

EngineHelper resolutionUnknown ID
InterpreterLooked up at runtime on each CALLruntime Err("unknown helper function (id: 0x..)")
x86_64 JITAddress embedded at jit_compile()compile-time error
CraneliftImported as symbol helper_<id> at cranelift_compile()compile-time error

For the JITs, register every helper before compiling. Registering a new key afterwards has no effect until you recompile. Replacing the function behind an existing key also needs a recompile, because the old function’s address is baked into the generated code.

Custom helper

use std::time::{SystemTime, UNIX_EPOCH}; fn helper_get_time_sec(_: u64, _: u64, _: u64, _: u64, _: u64) -> u64 { SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() } const HELPER_GET_TIME_SEC_IDX: u32 = 0x01; let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); vm.register_helper(HELPER_GET_TIME_SEC_IDX, helper_get_time_sec).unwrap(); let t = vm.execute_program().unwrap();

Helpers that return pointers

If a helper returns a pointer to host memory, such as a map value, the interpreter rejects any access through it unless you first whitelist the range:

let start = core::ptr::addr_of!(MAP_VALUE) as u64; vm.register_allowed_memory(start..start + core::mem::size_of::<Value>() as u64);

examples/allowed_memory.rs shows this end to end with a bpf_map_lookup_elem-style helper at index 1.

Built-in helpers (rbpf::helpers)

FunctionIndex constantstd onlyBehaviour
bpf_time_getnsBPF_KTIME_GETNS_IDX = 5yesMonotonic nanoseconds since the helper’s first call, not since boot
bpf_trace_printfBPF_TRACE_PRINTK_IDX = 6yesPrints args 3–5 as hex (bpf_trace_printf: 0x1, 0xf, 0x20). Returns the number of bytes written. Args 1–2 are ignored.
gather_bytes—noReturns arg1 << 32 | arg2 << 24 | arg3 << 16 | arg4 << 8 | arg5. For example (0x11, 0x22, 0x33, 0x44, 0x55) gives 0x1122334455.
memfrob—noXORs len bytes at ptr with 42 in place. Returns 0.
sqrti—yesInteger square root of arg 1 (via f64)
strcmp—noCompares two NUL-terminated strings (args 1–2). Returns 0 when equal, otherwise the absolute difference of the first differing bytes. Returns u64::MAX if either pointer is null.
rand—yesWyRand (thread-local, time/thread-seeded) value in [min, max] when min < max

The *_IDX constants match the Linux helper numbers, so programs compiled for the kernel can call these through the same IDs.

AArch64 compiler helpers

The AArch64 compiler does not use Helper or the HashMap. Every CALL imm (src = 0) becomes a call to the embedder’s RuntimeCallbacks.helper:

pub type HelperCallback = unsafe extern "C" fn(opaque: *mut c_void, id: u32, args: *const u64, out: *mut u64) -> u32;

The callback receives the helper ID and a pointer to the five argument values. It writes the result to *out and returns 0. Any other return value aborts execution with STATUS_CALLBACK_ERROR. The embedder decides which IDs are allowed and what each one does, in AxiomOS according to its verified helper and effect policy. See Invocation and callback ABI.

Last updated on