diff --git a/Makefile b/Makefile index 48d01a3..9b35dad 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,13 @@ compile: chmod +x ./tools/compile.py - ./tools/compile.py ./examples/execve.py + ./tools/compile.py ./examples/execve2.py install: pip install -e . clean: rm -rf build dist *.egg-info - rm -rf examples/execve.ll examples/execve.o + rm -rf examples/*.ll examples/*.o all: install compile diff --git a/README.md b/README.md index f1f2354..2ee4544 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,9 @@ Step 2. Run `make` to see the compilation output of the example. Step 3. Run `check.sh` to check if generated object file passes through the verifier inside the examples directory. Step 4. Run `make` in the `examples/c-form` directory to modify the example C BPF program to check the actual LLVM IR generated by clang. +### Development Notes +Run ` ./check.sh run execve2.o;` in examples folder + ## Authors - [@r41k0u](https://github.com/r41k0u) - [@varun-r-mallya](https://github.com/varun-r-mallya) diff --git a/examples/c-form/Makefile b/examples/c-form/Makefile index 90311c6..5fa5969 100644 --- a/examples/c-form/Makefile +++ b/examples/c-form/Makefile @@ -1,19 +1,19 @@ BPF_CLANG := clang CFLAGS := -O2 -emit-llvm -target bpf -c -SRC := ex3.bpf.c -OUT := ex3.bpf.ll -OBJECT := ex3.bpf.o +SRC := $(wildcard *.bpf.c) +LL := $(SRC:.bpf.c=.bpf.ll) +OBJ := $(SRC:.bpf.c=.bpf.o) .PHONY: all clean -all: $(OUT) - -object: $(SRC) - $(BPF_CLANG) -O2 -g -target bpf -c $< -o $(OBJECT) +all: $(LL) $(OBJ) -$(OUT): $(SRC) object +%.bpf.o: %.bpf.c + $(BPF_CLANG) -O2 -target bpf -c $< -o $@ + +%.bpf.ll: %.bpf.c $(BPF_CLANG) $(CFLAGS) -S $< -o $@ clean: - rm -f $(OUT) $(OBJECT) + rm -f $(LL) $(OBJ) diff --git a/examples/c-form/ex2.bpf.c b/examples/c-form/ex2.bpf.c index 756067a..8cc9a97 100644 --- a/examples/c-form/ex2.bpf.c +++ b/examples/c-form/ex2.bpf.c @@ -1,7 +1,7 @@ #include #include -SEC("kprobe/sys_clone") +SEC("tracepoint/syscalls/sys_enter_execve") int hello(struct pt_regs *ctx) { bpf_printk("Hello, World!\n"); return 0; diff --git a/examples/check.sh b/examples/check.sh index f236abe..46ec6d3 100755 --- a/examples/check.sh +++ b/examples/check.sh @@ -1,2 +1,33 @@ #!/bin/bash -sudo bpftool prog -d load $1 /sys/fs/bpf/tmp && sudo rm -f /sys/fs/bpf/tmp + +PIN_PATH="/sys/fs/bpf/bpf_prog" +FILE="$2" +case "$1" in + check) + echo "[*] Checking $FILE" + echo $(sudo bpftool prog load -d "$FILE" "$PIN_PATH") + sudo rm -f "$PIN_PATH" + echo "[+] Verification succeeded" + ;; + run) + echo "[*] Loading and running $FILE" + sudo bpftool prog load "$FILE" "$PIN_PATH" autoattach + echo "[+] Program loaded. Press Ctrl+C to stop" + sudo cat /sys/kernel/debug/tracing/trace_pipe + sudo rm -f "$PIN_PATH" + echo "[+] Stopped" + ;; + stop) + echo "[*] Stopping program" + sudo rm -f "$PIN_PATH" + echo "[+] Stopped" + ;; + *) + echo "Usage: $0 " + echo "Examples:" + echo " $0 check program.bpf.o" + echo " $0 run program.bpf.o" + echo " $0 stop" + exit 1 + ;; +esac diff --git a/examples/execve2.py b/examples/execve2.py index d388034..165a191 100644 --- a/examples/execve2.py +++ b/examples/execve2.py @@ -1,13 +1,11 @@ from pythonbpf.decorators import bpf, section -# from pythonbpf.decorators import tracepoint, syscalls from ctypes import c_void_p, c_int32 @bpf -@section("kprobe/sys_clone") +@section("tracepoint/syscalls/sys_enter_execve") def hello(ctx: c_void_p) -> c_int32: print("Hello, World!") return c_int32(0) - LICENSE = "GPL" diff --git a/examples/hello_world.py b/examples/hello_world.py index 5c2e061..742da67 100644 --- a/examples/hello_world.py +++ b/examples/hello_world.py @@ -1,11 +1,15 @@ -from pythonbpf.decorators import tracepoint, syscalls +# This is what it is going to look like +# pylint: disable-all# type: ignore +from pythonbpf.decorators import tracepoint, syscalls, bpfglobal, bpf from ctypes import c_void_p, c_int32 - +@bpf @tracepoint(syscalls.sys_clone) def trace_clone(ctx: c_void_p) -> c_int32: print("Hello, World!") return c_int32(0) - -LICENSE = "GPL" +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" diff --git a/pythonbpf/codegen.py b/pythonbpf/codegen.py index 6fc000d..921a553 100644 --- a/pythonbpf/codegen.py +++ b/pythonbpf/codegen.py @@ -1,8 +1,8 @@ import ast from llvmlite import ir from .license_pass import license_processing -from .functions_pass import func_proc, functions_processing -from .constants_pass import constants_processing +from .functions_pass import func_proc +# from .constants_pass import constants_processing from .globals_pass import globals_processing @@ -28,12 +28,10 @@ def processor(source_code, filename, module): func_proc(tree, module, bpf_chunks) # For now, we will parse the BPF specific parts of AST - # Big rewrite - # will worry later # constants_processing(tree, module) - # license_processing(tree, module) - # globals_processing(tree, module) + license_processing(tree, module) + globals_processing(tree, module) # functions_processing(tree, module) @@ -57,6 +55,7 @@ def compile_to_ir(filename: str, output: str): module.add_named_metadata("llvm.ident", ["llvmlite PythonBPF v0.0.0"]) with open(output, "w") as f: + f.write(f"source_filename = \"{filename}\"\n") f.write(str(module)) return output diff --git a/pythonbpf/functions_pass.py b/pythonbpf/functions_pass.py index cf72902..abe8466 100644 --- a/pythonbpf/functions_pass.py +++ b/pythonbpf/functions_pass.py @@ -13,9 +13,9 @@ def emit_function(module: ir.Module, name: str): param.add_attribute("nocapture") func.attributes.add("nounwind") -# func.attributes.add("\"frame-pointer\"=\"all\"") -# func.attributes.add("no-trapping-math", "true") -# func.attributes.add("stack-protector-buffer-size", "8") + # func.attributes.add("\"frame-pointer\"=\"all\"") + # func.attributes.add("no-trapping-math", "true") + # func.attributes.add("stack-protector-buffer-size", "8") block = func.append_basic_block(name="entry") builder = ir.IRBuilder(block) @@ -70,17 +70,17 @@ def process_func_body(module, builder, func_node, func): # Handle print statement for arg in call.args: if isinstance(arg, ast.Constant) and isinstance(arg.value, str): - fmt_str = arg.value + "\n" + fmt_str = arg.value + "\n" + "\0" # Create a global variable for the format string fmt_gvar = ir.GlobalVariable( module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=f"{func.name}____fmt") fmt_gvar.global_constant = True - fmt_gvar.initializer = ir.Constant( + fmt_gvar.initializer = ir.Constant( # type: ignore ir.ArrayType(ir.IntType(8), len(fmt_str)), bytearray(fmt_str.encode("utf8")) ) fmt_gvar.linkage = "internal" - fmt_gvar.align = 1 + fmt_gvar.align = 1 # type: ignore # Cast the global variable to i8* fmt_ptr = builder.bitcast( @@ -129,6 +129,8 @@ def process_bpf_chunk(func_node, module): func.linkage = "dso_local" func.attributes.add("nounwind") + func.attributes.add("noinline") + func.attributes.add("optnone") if func_node.args.args: # Only look at the first argument for now diff --git a/pythonbpf/globals_pass.py b/pythonbpf/globals_pass.py index df32327..859f546 100644 --- a/pythonbpf/globals_pass.py +++ b/pythonbpf/globals_pass.py @@ -31,8 +31,8 @@ def globals_processing(tree, module: ir.Module): collected = ["LICENSE"] for node in tree.body: - if isinstance(node, ast.FunctionDef) and len(node.decorator_list) == 1: - dec = node.decorator_list[0] + if isinstance(node, ast.FunctionDef) and len(node.decorator_list) == 2: + dec = node.decorator_list[1] if ( isinstance(dec, ast.Call) and isinstance(dec.func, ast.Name) diff --git a/tools/compile.py b/tools/compile.py index 1ee48e8..7a12159 100755 --- a/tools/compile.py +++ b/tools/compile.py @@ -14,7 +14,7 @@ def main(): 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) + subprocess.run(["llc", "-march=bpf", "-filetype=obj", "-O2", ll_file, "-o", o_file], check=True) if __name__ == "__main__": main()