Compiler Plugin Overview¶
Blog series: Part 5 — First steps with libbpf · Part 8 — Generating C code from Java · Part 11 — BTF and 13,000 generated Java classes · Part 12 — Write eBPF in pure Java

The compiler plugin is a javac Plugin (not an annotation processor) that intercepts the compilation of @BPF-annotated classes. It runs after annotation processing, walks the AST of each @BPFFunction-annotated method, and emits C. The annotation processor (Processor) generates an implementation class with a getByteCode() override; the compiler plugin fills in that bytecode by translating Java method bodies to C, invoking clang, and embedding the resulting .o as a jar resource.
Entry point¶
Class: me.bechberger.ebpf.bpf.compiler.CompilerPlugin
File: bpf-compiler-plugin/src/main/java/me/bechberger/ebpf/bpf/compiler/CompilerPlugin.java
The class implements com.sun.source.util.Plugin. javac discovers it via the SPI file:
That file contains a single line: me.bechberger.ebpf.bpf.compiler.CompilerPlugin.
javac calls CompilerPlugin.init(JavacTask task, String... args) (line 307) at the start of each compilation. The init method registers a TaskListener that fires after ANALYZE events. This ensures the full type hierarchy is resolved before translation begins.
Translation pipeline¶
- Discover
@BPFclasses. TheTaskListener.finished()callback callsgetBPFProgramImpls()on theCompilationUnitTreeto collect all classes that extendBPFProgramand carry the generated@BPFImplannotation. - Translate each
@BPFFunctionmethod.processBPFProgramImpl()iterates the methods. For each one it callsTranslator.translate(), which walks the javac AST and produces aCAST.Statement.FunctionDeclarationStatement. - Run
ArenaAssociationPass. After all methods are translated,ArenaAssociationPassexamines thedirectArenaRefsmap populated byTranslatorand injects per-arena association helper calls intostruct_opsentry points (CompilerPlugin.java:1037,ArenaAssociationPass.java). - Assemble and write the C file.
combineCode()hoists#includedirectives and primitiveSEC(".data")declarations before the function bodies (CompilerPlugin.java:1514–1536), then writes the result totarget/generated-sources/annotations/<ClassName>.bpf.c. - Invoke clang.
Processor.compile()callsfindNewestClangVersion()and runs clang with-O2 -g -std=gnu2y -target bpf(Processor.java:865). Clang 19+ is required. - Embed the object file. The compiled
.obytes are gzip-compressed, base64-encoded, and stored as astatic finalfield in the generated implementation class. For large programs the bytes are written as a class-path resource named<FQN>.oand loaded at runtime viagetByteCode()(Processor.java:1282).
Key classes¶
| Class | File | Role |
|---|---|---|
CompilerPlugin |
bpf-compiler-plugin/.../compiler/CompilerPlugin.java |
javac Plugin entry point; orchestrates discovery, translation, codegen, clang invocation |
Translator |
bpf-compiler-plugin/.../compiler/Translator.java |
Walks javac AST, converts Java statements and expressions to CAST nodes |
CAST |
bpf-compiler-plugin/.../cast/CAST.java (module cast) |
Immutable C AST; rendered to a C string by CAST.Statement.render() |
ArenaAssociationPass |
bpf-compiler-plugin/.../compiler/passes/ArenaAssociationPass.java |
Post-translation pass; wires @InArena map references to struct_ops program entries |
VerifierFixSuggester |
bpf-compiler-plugin/.../compiler/VerifierFixSuggester.java |
Converts a parsed verifier log entry into a four-part What/Why/Fix/See diagnostic hint |
Plugin flags¶
The plugin accepts a single argument block passed to -Xplugin:
| Flag | Example | Effect |
|---|---|---|
dumpC=true |
-Xplugin:"BPFCompilerPlugin dumpC=true" |
Write the generated .bpf.c to a file beside the source (default path: <ClassName>.bpf.c in the source directory) |
dumpC=false |
-Xplugin:"BPFCompilerPlugin dumpC=false" |
Suppress C dump (default) |
dumpC=<path> |
-Xplugin:"BPFCompilerPlugin dumpC=/tmp/out.c" |
Write the generated C to the given absolute path |
Source: CompilerPlugin.java:133,319.
The annotation processor accepts a separate -A flag:
| Flag | Example | Effect |
|---|---|---|
-Aebpf.folder=<dir> |
-Aebpf.folder=/my/project/src/bpf |
Base directory used when resolving EBPF_PROGRAM path constants (source: Processor.java:1007) |
Adding a new translation rule¶
- Find the AST node handler. Open
Translator.javaand locate thevisitXxxmethod orswitchcase that handles the Java construct you want to translate (e.g.visitMethodInvocation, expression dispatch intranslateExpression). - Add the case. Produce the appropriate
CASTnode. Emit alogger.printError(path, node, "message")for unsupported sub-cases rather than silently dropping them. - Write a test. Add a test class to
bpf-compiler-plugin-test/src/test/java/me/bechberger/ebpf/bpf/compiler/CompilerPluginTest.java. Tests compile a small@BPFclass viaInMemoryJavaCompilerand assert on the generated C string — no kernel or clang needed.
Next: Annotation Processor