Skip to Content
rbpfExamples

Examples

Relevant source files

ExampleRequiresShows
disassemblestddisassembler::disassemble on a hand-written program
to_json—disassembler::to_insn_vec + json crate: dump an ELF program as JSON
load_elfstdLoad the .classifier section of load_elf__block_a_port.o with the elf crate, run it on a TCP packet in EbpfVmFixedMbuff (offsets 0x40, 0x50), register bpf_trace_printf
datestdEbpfVmNoData, a custom helper (Unix time in seconds at index 1), interpreter vs. x86_64 JIT
allowed_memory—A map-lookup style helper returning a pointer to a static, plus register_allowed_memory. The eBPF object is built from the Aya project in examples/allowed-memory/.
rbpf_plugin—Plugin for bpf_conformance . Reads the program from stdin, takes memory as hex in the first argument, and accepts --jit, --cranelift and --program
cargo run --example disassemble cargo run --example load_elf cargo run --example allowed_memory cargo build --all-features --release --example rbpf_plugin

Loading programs from ELF

load_elf.rs explains why programs built for the kernel can need patching before rbpf runs them. The C source load_elf__block_a_port.c was compiled with:

clang -O2 -emit-llvm -c load_elf__block_a_port.c -o - | \ llc -march=bpf -filetype=obj -o load_elf__block_a_port.o

The kernel rewrites __sk_buff accesses. rbpf does not. The three 4-byte loads of the packet pointers (opcode 0x61) are therefore patched to 8-byte loads (0x79), and the data and data_end offsets moved to 0x40/0x50 so the 64-bit pointers do not overlap.

let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Fail to parse ELF file"); let hdr = file.section_header_by_name(".classifier").unwrap().unwrap(); let prog = file.section_data(&hdr).unwrap().0; let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); vm.register_helper(rbpf::helpers::BPF_TRACE_PRINTK_IDX, rbpf::helpers::bpf_trace_printf).unwrap(); let res = vm.execute_program(packet).unwrap();

Map-style helpers and allowed memory

const BPF_MAP_LOOKUP_ELEM_IDX: u32 = 1; static MAP_VALUE: Value = Value { result: 1 }; fn bpf_lookup_elem(_map: u64, key_addr: u64, _flags: u64, _u4: u64, _u5: u64) -> u64 { let key: Key = unsafe { *(key_addr as *const Key) }; if key.protocol == 1 { return core::ptr::addr_of!(MAP_VALUE) as u64; } 0 } let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); vm.register_helper(BPF_MAP_LOOKUP_ELEM_IDX, bpf_lookup_elem).unwrap(); let start = core::ptr::addr_of!(MAP_VALUE) as u64; vm.register_allowed_memory(start..start + core::mem::size_of::<Value>() as u64); assert_eq!(vm.execute_program().unwrap(), 1);

Without the register_allowed_memory call, the interpreter rejects the program’s read through the returned pointer as an out-of-bounds load.

AArch64 embedding

There is no example binary for the AArch64 compiler. The reference embedding is the Linux test harness in tests/aarch64_jit.rs (the native::Image type) together with the AxiomOS controller harness in tests/aarch64/managed.rs. See AArch64 JIT and Testing and CI.

Last updated on