The ecc (eunomia compiler) is a CO-RE (Compile Once - Run Everywhere) eBPF compiler toolchain that transforms eBPF C source code into distributable JSON/YAML packages. Focus on writing a single eBPF kernel program - nothing more!
ecc compiles eBPF programs and automatically generates:
- Portable packages with embedded BTF and compressed ELF
- Metadata extraction from source code documentation
- Type information for userspace data export
- Multiple output formats: JSON, YAML, standalone executables, WebAssembly headers
Note: This is the compiler toolchain. For a project template to build new eunomia-bpf applications, see eunomia-bpf-template. For the runtime, see eunomia-bpf runtime.
Download pre-built binary:
wget https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc
chmod +x ecc
sudo mv ecc /usr/local/bin/Or use Docker:
# For x86_64
docker run -it -v $(pwd):/src ghcr.io/eunomia-bpf/ecc-x86_64:latest
# For ARM64
docker run -it -v $(pwd):/src ghcr.io/eunomia-bpf/ecc-aarch64:latest# Compile eBPF program
ecc program.bpf.c
# With export event types
ecc program.bpf.c event.h
# Specify output directory
ecc program.bpf.c -o dist/
# Generate YAML instead of JSON
ecc program.bpf.c --yamlExample:
$ ecc examples/bpftools/bootstrap/bootstrap.bpf.c \
examples/bpftools/bootstrap/event.h
# Generates:
# - package.json (distributable package)
# - config.json (metadata)
# - output.bpf.o (compiled ELF)Input: program.bpf.c + event.h
↓
[1] Compile to BPF object
clang -target bpf -O2 -g → output.bpf.o
↓
[2] Extract skeleton metadata
bpftool gen skeleton -j → skeleton JSON
↓
[3] Extract BTF types
bpftool btf dump -j → type info
↓
[4] Parse documentation
clang AST → doc comments
↓
[5] Package & compress
zlib + base64 → package.json
↓
Output: Portable, distributable package
ecc program.bpf.c
# → package.jsonContains:
- Compressed ELF binary (base64-encoded)
- Program/map metadata
- BTF type information
- Documentation from source
Structure:
{
"bpf_skel": {
"obj_name": "bootstrap",
"data": "eJzt...base64(zlib(ELF))...",
"maps": [{...}],
"progs": [{...}]
},
"export_types": [{...}],
"eunomia_version": "1.0.0"
}ecc program.bpf.c --yaml
# → package.yamlSame content as JSON but in YAML format.
ecc program.bpf.c --standalone -o mytool
# → mytool (single executable)
./mytool # Run directlyProduces a self-contained executable with embedded eBPF program and runner.
ecc program.bpf.c --wasm-header
# → ebpf_program.hGenerates C header for Wasm integration:
const char* get_ebpf_program() {
return R"JSON({...})JSON";
}ecc program.bpf.c --btfgen
# → btfgen.tar.gzGenerates minimal BTF files for multiple kernel versions, enabling compatibility with older kernels without full BTF support.
Mark structs for automatic export with @export annotation:
/// @export
struct event {
u32 pid;
u32 ppid;
char comm[16];
};ecc will:
- Extract the struct from BTF
- Add type information to package
- Enable automatic userspace parsing
Document your eBPF programs with comments:
/// Trace process execution events
/// This program monitors when new processes are created
SEC("tracepoint/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx) {
// ...
}ecc extracts documentation and includes it in the package metadata.
# Add include paths
ecc program.bpf.c -a "-I/custom/include"
# Define macros
ecc program.bpf.c -a "-DDEBUG=1" -a "-DMAX_ENTRIES=1024"
# Use custom clang
ecc program.bpf.c --clang-bin /opt/clang-15/bin/clangGenerate tailored BTF for old kernels:
# Download btfhub archive and generate minimal BTF
ecc program.bpf.c --btfgen
# Use custom btfhub location
ecc program.bpf.c --btfgen --btfhub-archive /path/to/btfhubThis creates minimal BTF files for different kernel versions, significantly reducing size while maintaining compatibility.
clang -g -O2 -target bpf \
-D__TARGET_ARCH_x86 \
-I/usr/include/bpf \
-c program.bpf.c \
-o output.bpf.o
llvm-strip -g output.bpf.oKey flags:
-target bpf: Generate BPF bytecode-g: Include debug info (required for BTF)-O2: Optimization (required by BPF verifier)-D__TARGET_ARCH_xxx: Architecture macros
Skeleton generation:
bpftool gen skeleton output.bpf.o -j > skeleton.jsonExtracts:
- Program names and types
- Map definitions
- Attach point information
BTF extraction:
bpftool btf dump file output.bpf.o format c -j > btf.jsonExtracts:
- Type information
- Struct definitions
- Export types (marked with
@export)
Uses Clang AST to extract:
- Function documentation
- Parameter descriptions
- Usage notes
// Compress ELF
let compressed = zlib::encode(elf_data)?;
let encoded = base64::encode(compressed);
// Create package
let package = {
"bpf_skel": {
"data": encoded,
"maps": [...],
"progs": [...]
},
"export_types": [...],
"eunomia_version": "1.0.0"
};
fs::write("package.json", serde_json::to_string(&package)?)?;Result: Compressed package ~50% smaller than original ELF.
Ubuntu/Debian:
apt install clang libelf1 libelf-dev zlib1g-dev llvm
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shCentOS/Fedora:
dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh# Clone with submodules
git clone --recursive https://github.com/eunomia-bpf/eunomia-bpf.git
cd eunomia-bpf/compiler
# Or update submodules if already cloned
git submodule update --init --recursive --remote
# Build
make
# Install
sudo make install
# Verify
ecc -hcd compiler
make docker
# Use it
docker run -v $(pwd):/src ghcr.io/eunomia-bpf/ecc:latest program.bpf.ceunomia-bpf compiler
Usage: ecc [OPTIONS] <SOURCE_PATH> [EXPORT_EVENT_HEADER]
Arguments:
<SOURCE_PATH> Path to the .bpf.c source file
[EXPORT_EVENT_HEADER] Path to export event header (.h file)
Options:
-o, --output-path <DIR> Output directory for generated files
-v, --verbose Show detailed logs
-y, --yaml Output YAML instead of JSON
--wasm-header Generate WebAssembly header
-s, --standalone Generate standalone executable
-b, --btfgen Generate tailored BTF for old kernels
--btfhub-archive <PATH> BTFhub archive location
[default: ~/.eunomia/btfhub-archive]
-a, --additional-cflags <FLAG> Additional C compiler flags (repeatable)
-c, --clang-bin <PATH> Path to clang binary
[default: clang]
-l, --llvm-strip-bin <PATH> Path to llvm-strip binary
[default: llvm-strip]
--header-only Generate object for header definitions only
--no-generate-package-json Skip package.json generation
-h, --help Print help
bootstrap.bpf.c:
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
/// @export
struct event {
u32 pid;
char comm[16];
};
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} events SEC(".maps");
/// Trace process execution
SEC("tracepoint/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx) {
struct event *e;
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e) return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
bpf_get_current_comm(&e->comm, sizeof(e->comm));
bpf_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "GPL";Compile:
ecc bootstrap.bpf.c event.h
# Run with ecli
sudo ecli run package.jsonSEC("xdp")
int xdp_drop_tcp(struct xdp_md *ctx) {
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return XDP_PASS;
if (eth->h_proto != htons(ETH_P_IP))
return XDP_PASS;
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return XDP_PASS;
if (ip->protocol == IPPROTO_TCP)
return XDP_DROP;
return XDP_PASS;
}Compile:
ecc xdp_drop.bpf.c -o xdp/# Compile
ecc program.bpf.c
# Load and run
sudo ecli run package.json# Compile
ecc program.bpf.c
# Push to registry
ecli push package.json ghcr.io/yourorg/tool:v1.0
# Others can pull and run
ecli pull ghcr.io/yourorg/tool:v1.0
sudo ecli run ghcr.io/yourorg/tool:v1.0use std::process::Command;
// Compile eBPF program
let output = Command::new("ecc")
.args(&["program.bpf.c", "event.h", "-o", "dist/"])
.output()?;
if !output.status.success() {
eprintln!("Compilation failed: {}", String::from_utf8_lossy(&output.stderr));
}
// Load the generated package
let package = std::fs::read_to_string("dist/package.json")?;
// Use with bpf-loader-lib...Error: Failed to run clang: No such file or directory
Solution:
# Install clang
apt install clang # or dnf install clang
# Or specify path
ecc program.bpf.c --clang-bin /usr/bin/clang-15Error: Failed to dump BTF from compiled file
Solution:
- Ensure object compiled with
-g(ecc does this automatically) - Check bpftool is available:
which bpftool - Verify kernel has BTF support:
ls /sys/kernel/btf/vmlinux
Error: Struct 'event' marked with @export but not found in BTF
Solution: Ensure struct is actually used in code:
// Add unused pointer to force BTF emission
__attribute__((unused)) static struct event *__event_ptr;
/// @export
struct event {
u32 pid;
};Warning: package.json is 5MB, consider optimization
Solution:
- ecc strips debug symbols automatically
- Check for unnecessary includes
- Verify BTF data isn't duplicated
- Support tracepoints, fentry, kprobe, LSM
- Ring buffer and perf event output
- Easy compilation without code modification
- Wasm support
- JSON redesign
- libbpf features integration
- BTFgen for old kernels
- Enhanced XDP support
- Better uprobe support
- Improved old kernel compatibility
- Detailed Usage Guide - Complete documentation
- Template Project
- bpftool - BPF introspection tool
- libbpf - BPF CO-RE library
- CO-RE Guide
- eunomia-bpf Runtime - Load and run compiled packages
- ecli - CLI tool for running packages
MIT LICENSE - See LICENSE