How Does eBPF Work? A Deep Dive into Its VM, Instruction Set, and JIT
This article explains eBPF from a developer's perspective, covering the eBPF virtual machine, instruction encoding, bytecode generation with LLVM/Clang, loading via the bpf() syscall, verifier rules, execution flow, JIT compilation, and how Android builds and packages eBPF programs.
Overview of eBPF
eBPF (extended Berkeley Packet Filter) is the modern incarnation of the original BPF virtual machine. It runs programs inside a small VM that provides 11 registers (R0‑R10, where R10 is the frame pointer).
BPF Virtual Machine
The VM fetches, decodes, and executes 32‑bit eBPF instructions. Each instruction consists of an 8‑bit opcode, 8‑bit register specifier, 16‑bit offset, and a 32‑bit immediate value. Wide (64‑bit) instructions extend the immediate field with a reserved area and a second immediate.
Opcode Class
The low three bits of the opcode encode the class (type) of operation; the remaining five bits have class‑specific meanings.
Load/Store Instructions
Load/store opcodes are composed of a 3‑bit mode, a 3‑bit size, and a 3‑bit class field.
Arithmetic and Jump Instructions
Arithmetic/JMP opcodes contain a 4‑bit code, a 1‑bit source selector (K for immediate, X for register), and a 3‑bit class.
Compiling eBPF Bytecode
eBPF programs are compiled to ELF objects with machine type EM_BPF using Clang/LLVM. Example source bpfRingbufProg.c produces bpfRingbufProg.o.
llvm-readelf -h bpfRingbufProg.o llvm-objdump -d bpfRingbufProg.oLoading and Verifying BPF Programs
Loading
int prog_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr));Verification
The kernel verifier performs:
DAG analysis to ensure a directed‑acyclic control‑flow graph (no loops, unreachable code).
Path simulation to guarantee registers are initialized before use and to enforce register lifetimes across helper calls (R1‑R5 become unreadable after a call, R6‑R9 may be preserved).
Valid example:
bpf_mov R6, 1
bpf_call foo
bpf_mov R0, R6
bpf_exitUsing R1 after a call would fail verification because R1 is clobbered.
Executing eBPF Bytecode
Interpreter
The kernel executes eBPF via ___bpf_prog_run, which builds a 256‑entry jump table (one entry per possible opcode) and dispatches to the appropriate handler.
Example decoding of instruction 07 01 00 00 44 33 22 11:
code = 0 (ADD)
source = 0 (K = imm)
class = 7 (ALU64)
dst_reg = 1 (R1)
src_reg = 0
imm = 0x11223344
Resulting operation: R1 += 0x11223344.
JIT Compilation
For performance, the kernel can JIT‑compile eBPF bytecode to native ISA (e.g., AArch64). The core JIT routine resides in bpf_jit_comp.c; the function build_insn translates each eBPF instruction into target machine code.
Android eBPF Program Layout
Typical Android build workflow:
Compile cpufreq.bpf.c with Clang/LLVM to cpufreq.bpf.o (EM_BPF ELF).
Generate a C skeleton header with bpftool gen skeleton cpufreq.bpf.o > cpufreq.skel.h. The header defines const void *cpufreq_bpf__elf_bytes containing the bytecode as a constant array.
Link the skeleton header with the native program source ( cpufreq.c) and cross‑compile the whole package for the target architecture (e.g., AArch64).
The resulting executable embeds the eBPF ELF in its .rodata section. It can be extracted with dd and inspected with llvm-readelf or llvm-objdump:
dd if=cpufreq of=bpf_elf.bin bs=1 count=13561 skip=7269
llvm-readelf -h bpf_elf.bin
llvm-objdump -S bpf_elf.binThis workflow demonstrates the full lifecycle of an eBPF program on Android: source → ELF bytecode → skeleton header → native binary → embedded ELF → runtime loading via bpf() system call.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
OPPO Kernel Craftsman
Sharing Linux kernel-related cutting-edge technology, technical articles, technical news, and curated tutorials
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
