commit d5557d3c01d7b9152606b4f29f7d4801aa282750 Author: varun-r-mallya Date: Sat Aug 30 18:08:14 2025 +0530 Initialize PythonBPF project and toolchain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62321af --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*venv*/ +.ropeproject/ +.idea/ +*.egg-info/ +.vscode/ +__pycache__/ +*.ll +*.o \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e8d433a --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +install: + pip install -e . + +compile: + chmod +x ./tools/compile.py + ./tools/compile.py ./examples/execve.py + +clean: + rm -rf build dist *.egg-info + rm -rf examples/execve.ll examples/execve.o + +all: install compile + +.PHONY: all clean \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/examples/example.bpf.c b/examples/example.bpf.c new file mode 100644 index 0000000..c47700e --- /dev/null +++ b/examples/example.bpf.c @@ -0,0 +1,11 @@ +#include +#include + +SEC("tracepoint/syscalls/sys_enter_execve") +int trace_execve(void *ctx) +{ + bpf_printk("execve called\n"); + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/examples/execve.py b/examples/execve.py new file mode 100644 index 0000000..729db63 --- /dev/null +++ b/examples/execve.py @@ -0,0 +1,10 @@ +from pythonbpf import decorators + +@decorators.tracepoint("syscalls:sys_enter_execve") +def trace_execve(ctx) -> int: + decorators.trace_printk("execve called\n") + return 0 + +@decorators.license("GPL") +def _(): + pass diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..1c3312e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "pythonbpf" +version = "0.1.0" +description = "Reduced Python frontend for eBPF" +authors = [ + { name = "r41k0u" }, + { name = "varun-r-mallya" } +] +requires-python = ">=3.8" + +dependencies = [ + "llvmlite", + "astpretty" +] + +[tool.setuptools.packages.find] +where = ["."] +include = ["pythonbpf*"] diff --git a/pythonbpf/__init__.py b/pythonbpf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pythonbpf/codegen.py b/pythonbpf/codegen.py new file mode 100644 index 0000000..dc0fa22 --- /dev/null +++ b/pythonbpf/codegen.py @@ -0,0 +1,19 @@ +import ast +from llvmlite import ir + +def compile_to_ir(filename: str, output: str): + with open(filename) as f: + ast.parse(f.read(), filename) + + module = ir.Module(name="pythonbpf") + func_ty = ir.FunctionType(ir.IntType(64), [], False) + func = ir.Function(module, func_ty, name="trace_execve") + + block = func.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + builder.ret(ir.IntType(64)(0)) + + with open(output, "w") as f: + f.write(str(module)) + + return output diff --git a/pythonbpf/decorators.py b/pythonbpf/decorators.py new file mode 100644 index 0000000..d16b77e --- /dev/null +++ b/pythonbpf/decorators.py @@ -0,0 +1,15 @@ +def tracepoint(name: str): + def wrapper(fn): + fn._section = f"tracepoint/{name}" + return fn + return wrapper + +def license(name: str): + def wrapper(fn): + fn._license = name + return fn + return wrapper + +def trace_printk(msg: str): + # placeholder — real version lowers to IR later + print(f"[trace_printk] {msg}") diff --git a/tools/compile.py b/tools/compile.py new file mode 100755 index 0000000..1ee48e8 --- /dev/null +++ b/tools/compile.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +import argparse, subprocess, os +from pythonbpf import codegen + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("source", help="Python BPF program") + args = parser.parse_args() + + ll_file = os.path.splitext(args.source)[0] + ".ll" + o_file = os.path.splitext(args.source)[0] + ".o" + + print(f"[+] Compiling {args.source} → {ll_file}") + codegen.compile_to_ir(args.source, ll_file) + + print("[+] Running llc -march=bpf") + subprocess.run(["llc", "-march=bpf", "-filetype=obj", ll_file, "-o", o_file], check=True) + +if __name__ == "__main__": + main()