6 Commits

Author SHA1 Message Date
2ec67b6978 add globals todo 2025-09-30 14:48:51 +05:30
a2274ef29d deletions of demo 2025-09-30 14:29:55 +05:30
eb73001063 add debug info module 2025-09-30 14:29:20 +05:30
26f8f769c5 remove demos and add examples
Signed-off-by: varun-r-mallya <varunrmallya@gmail.com>
2025-09-29 23:44:49 +05:30
ea5a1ab2de add jupyter notebook support 2025-09-27 12:24:49 +05:30
de5cc438ab Allow access from struct fields 2025-09-26 23:02:51 +05:30
25 changed files with 1097 additions and 216 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@
__pycache__/ __pycache__/
*.ll *.ll
*.o *.o
.ipynb_checkpoints/

View File

@ -1,46 +0,0 @@
from __future__ import print_function
from bcc import BPF
from bcc.utils import printb
# load BPF program
b = BPF(text="""
#include <uapi/linux/ptrace.h>
BPF_HASH(last);
int do_trace(struct pt_regs *ctx) {
u64 ts, *tsp, delta, key = 0;
// attempt to read stored timestamp
tsp = last.lookup(&key);
if (tsp != NULL) {
delta = bpf_ktime_get_ns() - *tsp;
if (delta < 1000000000) {
// output if time is less than 1 second
bpf_trace_printk("%d\\n", delta / 1000000);
}
last.delete(&key);
}
// update stored timestamp
ts = bpf_ktime_get_ns();
last.update(&key, &ts);
return 0;
}
""")
b.attach_kprobe(event=b.get_syscall_fnname("sync"), fn_name="do_trace")
print("Tracing for quick sync's... Ctrl-C to end")
# TODO
# format output
start = 0
while 1:
try:
(task, pid, cpu, flags, ts, ms) = b.trace_fields()
if start == 0:
start = ts
ts = ts - start
printb(b"At time %.2f s: multiple syncs detected, last %s ms ago" % (ts, ms))
except KeyboardInterrupt:
exit()

View File

@ -1,23 +0,0 @@
from pythonbpf import bpf, section, bpfglobal, compile
from ctypes import c_void_p, c_int64
# Instructions to how to run this program
# 1. Install PythonBPF: pip install pythonbpf
# 2. Run the program: python demo/pybpf0.py
# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf0.o
# 4. Start up any program and watch the output
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def hello_world(ctx: c_void_p) -> c_int64:
print("Hello, World!")
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile()

View File

@ -1,52 +0,0 @@
from pythonbpf import bpf, map, section, bpfglobal, compile
from pythonbpf.helpers import ktime
from pythonbpf.maps import HashMap
from ctypes import c_void_p, c_int64, c_uint64
# Instructions to how to run this program
# 1. Install PythonBPF: pip install pythonbpf
# 2. Run the program: python demo/pybpf3.py
# 3. Run the program with sudo: sudo examples/check.sh run demo/pybpf3.o
# 4. Start up any program and watch the output
@bpf
@map
def last() -> HashMap:
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def do_trace(ctx: c_void_p) -> c_int64:
key = 0
tsp = last().lookup(key)
if tsp:
kt = ktime()
delta = (kt - tsp)
if delta < 1000000000:
time_ms = (delta // 1000000)
print(f"Execve syscall entered within last second, last {time_ms} ms ago")
last().delete(key)
else:
kt = ktime()
last().update(key, kt)
return c_int64(0)
@bpf
@section("tracepoint/syscalls/sys_exit_execve")
def do_exit(ctx: c_void_p) -> c_int64:
va = 8
nm = 5 ^ va
al = 6 & 3
ru = (nm + al)
print(f"this is a variable {ru}")
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile()

430
examples/IO-run.ipynb Normal file

File diff suppressed because one or more lines are too long

View File

@ -2,38 +2,39 @@
#include <bpf/bpf_helpers.h> #include <bpf/bpf_helpers.h>
#define u64 unsigned long long #define u64 unsigned long long
#define u32 unsigned int
// Define the map // Define the map
struct { struct {
__uint(type, BPF_MAP_TYPE_HASH); __uint(type, BPF_MAP_TYPE_HASH);
__type(key, u64); __type(key, u64);
__type(value, u64); __type(value, u32);
__uint(max_entries, 4); __uint(max_entries, 4);
} last SEC(".maps"); } last SEC(".maps");
// Handler for syscall entry // // Handler for syscall entry
SEC("tracepoint/syscalls/sys_enter_execve") // SEC("tracepoint/syscalls/sys_enter_execve")
int hello(void *ctx) { // int hello(void *ctx) {
bpf_printk("entered"); // bpf_printk("entered");
bpf_printk("multi constant support"); // bpf_printk("multi constant support");
return 0; // return 0;
} // }
// Handler for syscall exit // // Handler for syscall exit
SEC("tracepoint/syscalls/sys_exit_execve") // SEC("tracepoint/syscalls/sys_exit_execve")
long hello_again(void *ctx) { // long hello_again(void *ctx) {
bpf_printk("exited"); // bpf_printk("exited");
// Create a key for map lookup // // Create a key for map lookup
u64 key = 0; // u64 key = 0;
// Simple lookup without conditionals // // Simple lookup without conditionals
u64 *tsp = bpf_map_lookup_elem(&last, &key); // u64 *tsp = bpf_map_lookup_elem(&last, &key);
// Get current timestamp // // Get current timestamp
u64 ts = bpf_ktime_get_ns(); // u64 ts = bpf_ktime_get_ns();
return 0; // return 0;
} // }
char LICENSE[] SEC("license") = "GPL"; char LICENSE[] SEC("license") = "GPL";

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
from pythonbpf.decorators import bpf, map, section, bpfglobal from pythonbpf import bpf, map, section, bpfglobal, compile_to_ir
from ctypes import c_void_p, c_int64, c_int32, c_uint64 from ctypes import c_void_p, c_int64, c_int32, c_uint64
from pythonbpf.helpers import ktime from pythonbpf.helpers import ktime
from pythonbpf.maps import HashMap from pythonbpf.maps import HashMap
@ -10,26 +10,28 @@ def last() -> HashMap:
return HashMap(key=c_uint64, value=c_uint64, max_entries=1) return HashMap(key=c_uint64, value=c_uint64, max_entries=1)
@bpf # @bpf
@section("tracepoint/syscalls/sys_enter_execve") # @section("tracepoint/syscalls/sys_enter_execve")
def hello(ctx: c_void_p) -> c_int32: # def hello(ctx: c_void_p) -> c_int32:
print("entered") # print("entered")
print("multi constant support") # print("multi constant support")
return c_int32(0) # return c_int32(0)
@bpf # @bpf
@section("tracepoint/syscalls/sys_exit_execve") # @section("tracepoint/syscalls/sys_exit_execve")
def hello_again(ctx: c_void_p) -> c_int64: # def hello_again(ctx: c_void_p) -> c_int64:
print("exited") # print("exited")
key = 0 # key = 0
tsp = last().lookup(key) # tsp = last().lookup(key)
print(tsp) # print(tsp)
ts = ktime() # ts = ktime()
return c_int64(0) # return c_int64(0)
@bpf @bpf
@bpfglobal @bpfglobal
def LICENSE() -> str: def LICENSE() -> str:
return "GPL" return "GPL"
compile_to_ir("execve2.py", "execve2.ll")

View File

@ -26,10 +26,10 @@ def hello(ctx: c_void_p) -> c_int32:
ts = ktime() ts = ktime()
process_id = pid() process_id = pid()
strobj = "hellohellohello" strobj = "hellohellohello"
dataobj.pid = process_id dataobj.pid = pid()
dataobj.ts = ts dataobj.ts = ktime()
# dataobj.comm = strobj # dataobj.comm = strobj
print(f"clone called at {ts} by pid {process_id}, comm {strobj}") print(f"clone called at {dataobj.ts} by pid {dataobj.pid}, comm {strobj}")
events.output(dataobj) events.output(dataobj)
return c_int32(0) return c_int32(0)

View File

@ -1,15 +0,0 @@
# 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)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"

35
examples/pybpf0.py Normal file
View File

@ -0,0 +1,35 @@
from pythonbpf import bpf, section, bpfglobal, BPF
import sys
from ctypes import c_void_p, c_int64
# Instructions to how to run this program
# 1. Install PythonBPF: pip install pythonbpf
# 2. `sudo /path/to/venv/bin/python ./python-bpf/demo/pybpf0.py`
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def hello_world(ctx: c_void_p) -> c_int64:
print("Hello, World!")
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
b = BPF()
b.load_and_attach()
def main():
try:
with open("/sys/kernel/debug/tracing/trace_pipe", "r") as f:
for line in f:
sys.stdout.write(line)
sys.stdout.flush()
except KeyboardInterrupt:
pass
except PermissionError:
sys.stderr.write("Need root privileges to read trace_pipe\n")
if __name__ == "__main__":
main()

49
examples/pybpf3.py Normal file
View File

@ -0,0 +1,49 @@
from pythonbpf import *
from pylibbpf import *
import sys
from ctypes import c_void_p, c_int64, c_uint64
@bpf
@map
def last() -> HashMap:
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
@bpf
@section("tracepoint/syscalls/sys_enter_clone")
def do_trace(ctx: c_void_p) -> c_int64:
key = 0
tsp = last().lookup(key)
if tsp:
kt = ktime()
delta = (kt - tsp)
if delta < 1000000000:
time_ms = (delta // 1000000)
print(f"Clone syscall entered within last second, last {time_ms} ms ago")
last().delete(key)
else:
kt = ktime()
last().update(key, kt)
return c_int64(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
b = BPF()
# autoattaches tracepoints
b.load_and_attach()
def main():
try:
with open("/sys/kernel/debug/tracing/trace_pipe", "r") as f:
for line in f:
sys.stdout.write(line)
sys.stdout.flush()
except KeyboardInterrupt:
pass
except PermissionError:
sys.stderr.write("Need root privileges to read trace_pipe\n")
if __name__ == "__main__":
main()

View File

@ -3,7 +3,8 @@ import time
from pythonbpf import bpf, map, section, bpfglobal, BPF from pythonbpf import bpf, map, section, bpfglobal, BPF
from pythonbpf.helpers import pid from pythonbpf.helpers import pid
from pythonbpf.maps import HashMap from pythonbpf.maps import HashMap
from pylibbpf import * from pylibbpf import BpfMap
from ctypes import c_void_p, c_int64, c_uint64, c_int32 from ctypes import c_void_p, c_int64, c_uint64, c_int32
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
@ -40,7 +41,6 @@ def hello(ctx: c_void_p) -> c_int64:
def LICENSE() -> str: def LICENSE() -> str:
return "GPL" return "GPL"
b = BPF() b = BPF()
b.load_and_attach() b.load_and_attach()
hist = BpfMap(b, hist) hist = BpfMap(b, hist)
@ -48,9 +48,15 @@ print("Recording")
time.sleep(10) time.sleep(10)
counts = list(hist.values()) counts = list(hist.values())
x = 0
for key in hist.keys():
if hist[key] > 40:
x += 1
print(f"PID {key} called clone() >40 times")
print(f"Total PIDs with clone() >40 times: {x}")
plt.hist(counts, bins=20) plt.hist(counts, bins=20)
plt.xlabel("Clone calls per PID") plt.xlabel("Clone calls per PID")
plt.ylabel("Frequency") plt.ylabel("Number of processes that called clone() x times in last 10 seconds")
plt.title("Syscall clone counts") plt.title("x")
plt.show() plt.show()

View File

@ -1,2 +1,4 @@
from .decorators import bpf, map, section, bpfglobal, struct from .decorators import bpf, map, section, bpfglobal, struct
from .codegen import compile_to_ir, compile, BPF from .codegen import compile_to_ir, compile, BPF
from .maps import HashMap, PerfEventArray
from .helpers import pid, XDP_DROP, XDP_PASS, ktime, deref

View File

@ -3,7 +3,7 @@ from llvmlite import ir
from .expr_pass import eval_expr from .expr_pass import eval_expr
def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, local_var_metadata=None): def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None):
""" """
Emit LLVM IR for bpf_ktime_get_ns helper function call. Emit LLVM IR for bpf_ktime_get_ns helper function call.
""" """
@ -63,7 +63,7 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, local_sym_
return result, ir.PointerType() return result, ir.PointerType()
def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, local_var_metadata=None): def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None):
if not hasattr(func, "_fmt_counter"): if not hasattr(func, "_fmt_counter"):
func._fmt_counter = 0 func._fmt_counter = 0
@ -101,10 +101,42 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None,
else: else:
raise NotImplementedError( raise NotImplementedError(
"Only integer and pointer types are supported in formatted values.") "Only integer and pointer types are supported in formatted values.")
print("Formatted value variable:", var_ptr, var_type)
else: else:
raise ValueError( raise ValueError(
f"Variable {value.value.id} not found in local symbol table.") f"Variable {value.value.id} not found in local symbol table.")
elif isinstance(value.value, ast.Attribute):
# object field access from struct
if isinstance(value.value.value, ast.Name) and local_sym_tab and value.value.value.id in local_sym_tab:
var_name = value.value.value.id
field_name = value.value.attr
if local_var_metadata and var_name in local_var_metadata:
var_type = local_var_metadata[var_name]
if var_type in struct_sym_tab:
struct_info = struct_sym_tab[var_type]
if field_name in struct_info["fields"]:
field_index = struct_info["fields"][field_name]
field_type = struct_info["field_types"][field_index]
if isinstance(field_type, ir.IntType):
fmt_parts.append("%lld")
exprs.append(value.value)
elif field_type == ir.PointerType(ir.IntType(8)):
fmt_parts.append("%s")
exprs.append(value.value)
else:
raise NotImplementedError(
"Only integer and pointer types are supported in formatted values.")
else:
raise ValueError(
f"Field {field_name} not found in struct {var_type}.")
else:
raise ValueError(
f"Struct type {var_type} for variable {var_name} not found in struct symbol table.")
else:
raise ValueError(
f"Metadata for variable {var_name} not found in local variable metadata.")
else:
raise ValueError(
f"Variable {value.value.value.id} not found in local symbol table.")
else: else:
raise NotImplementedError( raise NotImplementedError(
"Only simple variable names are supported in formatted values.") "Only simple variable names are supported in formatted values.")
@ -119,12 +151,12 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None,
fmt_gvar = ir.GlobalVariable( fmt_gvar = ir.GlobalVariable(
module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=fmt_name) module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=fmt_name)
fmt_gvar.global_constant = True 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)), ir.ArrayType(ir.IntType(8), len(fmt_str)),
bytearray(fmt_str.encode("utf8")) bytearray(fmt_str.encode("utf8"))
) )
fmt_gvar.linkage = "internal" fmt_gvar.linkage = "internal"
fmt_gvar.align = 1 fmt_gvar.align = 1 # type: ignore
fmt_ptr = builder.bitcast(fmt_gvar, ir.PointerType()) fmt_ptr = builder.bitcast(fmt_gvar, ir.PointerType())
@ -136,8 +168,9 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None,
"Warning: bpf_printk supports up to 3 arguments, extra arguments will be ignored.") "Warning: bpf_printk supports up to 3 arguments, extra arguments will be ignored.")
for expr in exprs[:3]: for expr in exprs[:3]:
print(f"{ast.dump(expr)}")
val, _ = eval_expr(func, module, builder, val, _ = eval_expr(func, module, builder,
expr, local_sym_tab, None) expr, local_sym_tab, None, struct_sym_tab, local_var_metadata)
if val: if val:
if isinstance(val.type, ir.PointerType): if isinstance(val.type, ir.PointerType):
val = builder.ptrtoint(val, ir.IntType(64)) val = builder.ptrtoint(val, ir.IntType(64))
@ -339,7 +372,7 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, local_sym_
return result, None return result, None
def bpf_get_current_pid_tgid_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, local_var_metadata=None): def bpf_get_current_pid_tgid_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None):
""" """
Emit LLVM IR for bpf_get_current_pid_tgid helper function call. Emit LLVM IR for bpf_get_current_pid_tgid helper function call.
""" """
@ -420,11 +453,12 @@ helper_func_list = {
def handle_helper_call(call, module, builder, func, local_sym_tab=None, map_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): def handle_helper_call(call, module, builder, func, local_sym_tab=None, map_sym_tab=None, struct_sym_tab=None, local_var_metadata=None):
print(local_var_metadata)
if isinstance(call.func, ast.Name): if isinstance(call.func, ast.Name):
func_name = call.func.id func_name = call.func.id
if func_name in helper_func_list: if func_name in helper_func_list:
# it is not a map method call # it is not a map method call
return helper_func_list[func_name](call, None, module, builder, func, local_sym_tab) return helper_func_list[func_name](call, None, module, builder, func, local_sym_tab, struct_sym_tab, local_var_metadata)
else: else:
raise NotImplementedError( raise NotImplementedError(
f"Function {func_name} is not implemented as a helper function.") f"Function {func_name} is not implemented as a helper function.")

View File

@ -1,5 +1,7 @@
import ast import ast
from llvmlite import ir from llvmlite import ir
from .debuginfo import DW_LANG_C11, DwarfBehaviorEnum
from .license_pass import license_processing from .license_pass import license_processing
from .functions_pass import func_proc from .functions_pass import func_proc
from .maps_pass import maps_proc from .maps_pass import maps_proc
@ -10,7 +12,9 @@ import subprocess
import inspect import inspect
from pathlib import Path from pathlib import Path
from pylibbpf import BpfProgram from pylibbpf import BpfProgram
import tempfile
version = "v0.1.3"
def find_bpf_chunks(tree): def find_bpf_chunks(tree):
"""Find all functions decorated with @bpf in the AST.""" """Find all functions decorated with @bpf in the AST."""
@ -49,16 +53,18 @@ def compile_to_ir(filename: str, output: str):
module.triple = "bpf" module.triple = "bpf"
if not hasattr(module, '_debug_compile_unit'): if not hasattr(module, '_debug_compile_unit'):
module._file_metadata = module.add_debug_info("DIFile", { # type: ignore module._file_metadata = module.add_debug_info("DIFile", { # type: ignore
"filename": filename, "filename": filename,
"directory": os.path.dirname(filename) "directory": os.path.dirname(filename)
}) })
module._debug_compile_unit = module.add_debug_info("DICompileUnit", { # type: ignore module._debug_compile_unit = module.add_debug_info("DICompileUnit", { # type: ignore
"language": 29, # DW_LANG_C11 "language": DW_LANG_C11,
"file": module._file_metadata, # type: ignore "file": module._file_metadata, # type: ignore
"producer": "PythonBPF DSL Compiler", "producer": f"PythonBPF {version}",
"isOptimized": True, "isOptimized": True, #TODO: This is probably not true
#TODO: add a global field here that keeps track of all the globals. Works without it, but I think it might
# be required for kprobes.
"runtimeVersion": 0, "runtimeVersion": 0,
"emissionKind": 1, "emissionKind": 1,
"splitDebugInlining": False, "splitDebugInlining": False,
@ -66,32 +72,32 @@ def compile_to_ir(filename: str, output: str):
}, is_distinct=True) }, is_distinct=True)
module.add_named_metadata( module.add_named_metadata(
"llvm.dbg.cu", module._debug_compile_unit) # type: ignore "llvm.dbg.cu", module._debug_compile_unit) # type: ignore
processor(source, filename, module) processor(source, filename, module)
wchar_size = module.add_metadata([ir.Constant(ir.IntType(32), 1), wchar_size = module.add_metadata([DwarfBehaviorEnum.ERROR_IF_MISMATCH,
"wchar_size", "wchar_size",
ir.Constant(ir.IntType(32), 4)]) ir.Constant(ir.IntType(32), 4)])
frame_pointer = module.add_metadata([ir.Constant(ir.IntType(32), 7), frame_pointer = module.add_metadata([DwarfBehaviorEnum.OVERRIDE_USE_LARGEST,
"frame-pointer", "frame-pointer",
ir.Constant(ir.IntType(32), 2)]) ir.Constant(ir.IntType(32), 2)])
# Add Debug Info Version (3 = DWARF v3, which LLVM expects) # Add Debug Info Version 3
debug_info_version = module.add_metadata([ir.Constant(ir.IntType(32), 2), debug_info_version = module.add_metadata([DwarfBehaviorEnum.WARNING_IF_MISMATCH,
"Debug Info Version", "Debug Info Version",
ir.Constant(ir.IntType(32), 3)]) ir.Constant(ir.IntType(32), 3)])
# Add explicit DWARF version (4 is common, works with LLVM BPF backend) # Add explicit DWARF version 5
dwarf_version = module.add_metadata([ir.Constant(ir.IntType(32), 2), dwarf_version = module.add_metadata([DwarfBehaviorEnum.OVERRIDE_USE_LARGEST,
"Dwarf Version", "Dwarf Version",
ir.Constant(ir.IntType(32), 4)]) ir.Constant(ir.IntType(32), 5)])
module.add_named_metadata("llvm.module.flags", wchar_size) module.add_named_metadata("llvm.module.flags", wchar_size)
module.add_named_metadata("llvm.module.flags", frame_pointer) module.add_named_metadata("llvm.module.flags", frame_pointer)
module.add_named_metadata("llvm.module.flags", debug_info_version) module.add_named_metadata("llvm.module.flags", debug_info_version)
module.add_named_metadata("llvm.module.flags", dwarf_version) module.add_named_metadata("llvm.module.flags", dwarf_version)
module.add_named_metadata("llvm.ident", ["llvmlite PythonBPF v0.0.1"]) module.add_named_metadata("llvm.ident", [f"PythonBPF {version}"])
print(f"IR written to {output}") print(f"IR written to {output}")
with open(output, "w") as f: with open(output, "w") as f:
@ -122,14 +128,17 @@ def compile():
def BPF() -> BpfProgram: def BPF() -> BpfProgram:
caller_frame = inspect.stack()[1] caller_frame = inspect.stack()[1]
caller_file = Path(caller_frame.filename).resolve() src = inspect.getsource(caller_frame.frame)
ll_file = Path("/tmp") / caller_file.with_suffix(".ll").name with tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".py") as f, \
o_file = Path("/tmp") / caller_file.with_suffix(".o").name tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".ll") as inter, \
compile_to_ir(str(caller_file), str(ll_file)) tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".o") as obj_file:
f.write(src)
f.flush()
source = f.name
compile_to_ir(source, str(inter.name))
subprocess.run([
"llc", "-march=bpf", "-filetype=obj", "-O2",
str(inter.name), "-o", str(obj_file.name)
], check=True)
subprocess.run([ return BpfProgram(str(obj_file.name))
"llc", "-march=bpf", "-filetype=obj", "-O2",
str(ll_file), "-o", str(o_file)
], check=True)
return BpfProgram(str(o_file))

View File

@ -0,0 +1,2 @@
from .dwarf_constants import *
from .dtypes import *

View File

@ -0,0 +1,6 @@
import llvmlite.ir as ir
class DwarfBehaviorEnum:
ERROR_IF_MISMATCH = ir.Constant(ir.IntType(32), 1)
WARNING_IF_MISMATCH = ir.Constant(ir.IntType(32), 2)
OVERRIDE_USE_LARGEST = ir.Constant(ir.IntType(32), 7)

View File

@ -4,6 +4,7 @@ from llvmlite import ir
def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab=None, local_var_metadata=None): def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab=None, local_var_metadata=None):
print(f"Evaluating expression: {ast.dump(expr)}") print(f"Evaluating expression: {ast.dump(expr)}")
print(local_var_metadata)
if isinstance(expr, ast.Name): if isinstance(expr, ast.Name):
if expr.id in local_sym_tab: if expr.id in local_sym_tab:
var = local_sym_tab[expr.id][0] var = local_sym_tab[expr.id][0]
@ -66,6 +67,25 @@ def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_s
if method_name in helper_func_list: if method_name in helper_func_list:
return handle_helper_call( return handle_helper_call(
expr, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) expr, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata)
elif isinstance(expr, ast.Attribute):
if isinstance(expr.value, ast.Name):
var_name = expr.value.id
attr_name = expr.attr
if var_name in local_sym_tab:
var_ptr, var_type = local_sym_tab[var_name]
print(f"Loading attribute "
f"{attr_name} from variable {var_name}")
print(f"Variable type: {var_type}, Variable ptr: {var_ptr}")
print(local_var_metadata)
if local_var_metadata and var_name in local_var_metadata:
metadata = structs_sym_tab[local_var_metadata[var_name]]
if attr_name in metadata["fields"]:
field_idx = metadata["fields"][attr_name]
gep = builder.gep(var_ptr, [ir.Constant(ir.IntType(32), 0),
ir.Constant(ir.IntType(32), field_idx)])
val = builder.load(gep)
field_type = metadata["field_types"][field_idx]
return val, field_type
print("Unsupported expression evaluation") print("Unsupported expression evaluation")
return None return None
@ -73,6 +93,7 @@ def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_s
def handle_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata): def handle_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata):
"""Handle expression statements in the function body.""" """Handle expression statements in the function body."""
print(f"Handling expression: {ast.dump(expr)}") print(f"Handling expression: {ast.dump(expr)}")
print(local_var_metadata)
call = expr.value call = expr.value
if isinstance(call, ast.Call): if isinstance(call, ast.Call):
eval_expr(func, module, builder, call, local_sym_tab, eval_expr(func, module, builder, call, local_sym_tab,

View File

@ -282,6 +282,7 @@ def handle_if(func, module, builder, stmt, map_sym_tab, local_sym_tab, structs_s
def process_stmt(func, module, builder, stmt, local_sym_tab, map_sym_tab, structs_sym_tab, did_return, ret_type=ir.IntType(64)): def process_stmt(func, module, builder, stmt, local_sym_tab, map_sym_tab, structs_sym_tab, did_return, ret_type=ir.IntType(64)):
print(f"Processing statement: {ast.dump(stmt)}") print(f"Processing statement: {ast.dump(stmt)}")
if isinstance(stmt, ast.Expr): if isinstance(stmt, ast.Expr):
print(local_var_metadata)
handle_expr(func, module, builder, stmt, local_sym_tab, handle_expr(func, module, builder, stmt, local_sym_tab,
map_sym_tab, structs_sym_tab, local_var_metadata) map_sym_tab, structs_sym_tab, local_var_metadata)
elif isinstance(stmt, ast.Assign): elif isinstance(stmt, ast.Assign):

View File

@ -1,7 +1,6 @@
import ast import ast
from llvmlite import ir from llvmlite import ir
from .type_deducer import ctypes_to_ir from .debuginfo import dwarf_constants as dc
from . import dwarf_constants as dc
map_sym_tab = {} map_sym_tab = {}

View File

@ -1,7 +1,6 @@
import ast import ast
from llvmlite import ir from llvmlite import ir
from .type_deducer import ctypes_to_ir from .type_deducer import ctypes_to_ir
from . import dwarf_constants as dc
structs_sym_tab = {} structs_sym_tab = {}