3 Commits

11 changed files with 290 additions and 119 deletions

View File

@ -41,14 +41,14 @@ repos:
- id: ruff - id: ruff
args: ["--fix", "--show-fixes"] args: ["--fix", "--show-fixes"]
- id: ruff-format - id: ruff-format
exclude: ^(docs)|^(tests)|^(examples) exclude: ^(tests/|examples/|docs/)
# Checking static types # Checking static types
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.10.0" rev: "v1.10.0"
hooks: hooks:
- id: mypy - id: mypy
exclude: ^(tests)|^(examples) exclude: ^(tests/|examples/)
additional_dependencies: [types-setuptools] additional_dependencies: [types-setuptools]
# Changes tabs to spaces # Changes tabs to spaces

View File

@ -22,9 +22,5 @@ def LICENSE() -> str:
b = BPF() b = BPF()
b.load_and_attach() b.load_and_attach()
if b.is_loaded() and b.is_attached():
print("Successfully loaded and attached")
else:
print("Could not load successfully")
# Now cat /sys/kernel/debug/tracing/trace_pipe to see results of the execve syscall. # Now cat /sys/kernel/debug/tracing/trace_pipe to see results of the execve syscall.

View File

@ -25,7 +25,7 @@ def handle_binary_op(rval, module, builder, var_name, local_sym_tab, map_sym_tab
# Handle left operand # Handle left operand
if isinstance(left, ast.Name): if isinstance(left, ast.Name):
if left.id in local_sym_tab: if left.id in local_sym_tab:
left = recursive_dereferencer(local_sym_tab[left.id].var, builder) left = recursive_dereferencer(local_sym_tab[left.id][0], builder)
else: else:
raise SyntaxError(f"Undefined variable: {left.id}") raise SyntaxError(f"Undefined variable: {left.id}")
elif isinstance(left, ast.Constant): elif isinstance(left, ast.Constant):
@ -35,7 +35,7 @@ def handle_binary_op(rval, module, builder, var_name, local_sym_tab, map_sym_tab
if isinstance(right, ast.Name): if isinstance(right, ast.Name):
if right.id in local_sym_tab: if right.id in local_sym_tab:
right = recursive_dereferencer(local_sym_tab[right.id].var, builder) right = recursive_dereferencer(local_sym_tab[right.id][0], builder)
else: else:
raise SyntaxError(f"Undefined variable: {right.id}") raise SyntaxError(f"Undefined variable: {right.id}")
elif isinstance(right, ast.Constant): elif isinstance(right, ast.Constant):
@ -46,26 +46,26 @@ def handle_binary_op(rval, module, builder, var_name, local_sym_tab, map_sym_tab
print(f"left is {left}, right is {right}, op is {op}") print(f"left is {left}, right is {right}, op is {op}")
if isinstance(op, ast.Add): if isinstance(op, ast.Add):
builder.store(builder.add(left, right), local_sym_tab[var_name].var) builder.store(builder.add(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.Sub): elif isinstance(op, ast.Sub):
builder.store(builder.sub(left, right), local_sym_tab[var_name].var) builder.store(builder.sub(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.Mult): elif isinstance(op, ast.Mult):
builder.store(builder.mul(left, right), local_sym_tab[var_name].var) builder.store(builder.mul(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.Div): elif isinstance(op, ast.Div):
builder.store(builder.sdiv(left, right), local_sym_tab[var_name].var) builder.store(builder.sdiv(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.Mod): elif isinstance(op, ast.Mod):
builder.store(builder.srem(left, right), local_sym_tab[var_name].var) builder.store(builder.srem(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.LShift): elif isinstance(op, ast.LShift):
builder.store(builder.shl(left, right), local_sym_tab[var_name].var) builder.store(builder.shl(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.RShift): elif isinstance(op, ast.RShift):
builder.store(builder.lshr(left, right), local_sym_tab[var_name].var) builder.store(builder.lshr(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.BitOr): elif isinstance(op, ast.BitOr):
builder.store(builder.or_(left, right), local_sym_tab[var_name].var) builder.store(builder.or_(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.BitXor): elif isinstance(op, ast.BitXor):
builder.store(builder.xor(left, right), local_sym_tab[var_name].var) builder.store(builder.xor(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.BitAnd): elif isinstance(op, ast.BitAnd):
builder.store(builder.and_(left, right), local_sym_tab[var_name].var) builder.store(builder.and_(left, right), local_sym_tab[var_name][0])
elif isinstance(op, ast.FloorDiv): elif isinstance(op, ast.FloorDiv):
builder.store(builder.udiv(left, right), local_sym_tab[var_name].var) builder.store(builder.udiv(left, right), local_sym_tab[var_name][0])
else: else:
raise SyntaxError("Unsupported binary operation") raise SyntaxError("Unsupported binary operation")

View File

@ -10,13 +10,15 @@ def eval_expr(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_sym_tab=None, 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].var var = local_sym_tab[expr.id][0]
val = builder.load(var) val = builder.load(var)
return val, local_sym_tab[expr.id].ir_type # return value and type return val, local_sym_tab[expr.id][1] # return value and type
else: else:
print(f"Undefined variable {expr.id}") print(f"Undefined variable {expr.id}")
return None return None
@ -49,7 +51,7 @@ def eval_expr(
return None return None
if isinstance(arg, ast.Name): if isinstance(arg, ast.Name):
if arg.id in local_sym_tab: if arg.id in local_sym_tab:
arg = local_sym_tab[arg.id].var arg = local_sym_tab[arg.id][0]
else: else:
print(f"Undefined variable {arg.id}") print(f"Undefined variable {arg.id}")
return None return None
@ -58,7 +60,7 @@ def eval_expr(
return None return None
# Since we are handling only name case, directly take type from sym tab # Since we are handling only name case, directly take type from sym tab
val = builder.load(arg) val = builder.load(arg)
return val, local_sym_tab[expr.args[0].id].ir_type return val, local_sym_tab[expr.args[0].id][1]
# check for helpers # check for helpers
if HelperHandlerRegistry.has_handler(expr.func.id): if HelperHandlerRegistry.has_handler(expr.func.id):
@ -70,6 +72,7 @@ def eval_expr(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_sym_tab, structs_sym_tab,
local_var_metadata,
) )
elif isinstance(expr.func, ast.Attribute): elif isinstance(expr.func, ast.Attribute):
print(f"Handling method call: {ast.dump(expr.func)}") print(f"Handling method call: {ast.dump(expr.func)}")
@ -86,6 +89,7 @@ def eval_expr(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_sym_tab, structs_sym_tab,
local_var_metadata,
) )
elif isinstance(expr.func.value, ast.Name): elif isinstance(expr.func.value, ast.Name):
obj_name = expr.func.value.id obj_name = expr.func.value.id
@ -100,16 +104,19 @@ def eval_expr(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_sym_tab, structs_sym_tab,
local_var_metadata,
) )
elif isinstance(expr, ast.Attribute): elif isinstance(expr, ast.Attribute):
if isinstance(expr.value, ast.Name): if isinstance(expr.value, ast.Name):
var_name = expr.value.id var_name = expr.value.id
attr_name = expr.attr attr_name = expr.attr
if var_name in local_sym_tab: if var_name in local_sym_tab:
var_ptr, var_type, var_metadata = local_sym_tab[var_name] var_ptr, var_type = local_sym_tab[var_name]
print(f"Loading attribute {attr_name} from variable {var_name}") print(f"Loading attribute " f"{attr_name} from variable {var_name}")
print(f"Variable type: {var_type}, Variable ptr: {var_ptr}") print(f"Variable type: {var_type}, Variable ptr: {var_ptr}")
metadata = structs_sym_tab[var_metadata] 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: if attr_name in metadata.fields:
gep = metadata.gep(builder, var_ptr, attr_name) gep = metadata.gep(builder, var_ptr, attr_name)
val = builder.load(gep) val = builder.load(gep)
@ -127,9 +134,11 @@ def handle_expr(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_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( eval_expr(
@ -140,6 +149,7 @@ def handle_expr(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_sym_tab, structs_sym_tab,
local_var_metadata,
) )
else: else:
print("Unsupported expression type") print("Unsupported expression type")

View File

@ -1,27 +1,13 @@
from llvmlite import ir from llvmlite import ir
import ast import ast
import logging
from typing import Any from typing import Any
from dataclasses import dataclass
from .helper import HelperHandlerRegistry, handle_helper_call from .helper import HelperHandlerRegistry, handle_helper_call
from .type_deducer import ctypes_to_ir from .type_deducer import ctypes_to_ir
from .binary_ops import handle_binary_op from .binary_ops import handle_binary_op
from .expr_pass import eval_expr, handle_expr from .expr_pass import eval_expr, handle_expr
logger = logging.getLogger(__name__) local_var_metadata: dict[str | Any, Any] = {}
@dataclass
class LocalSymbol:
var: ir.AllocaInstr
ir_type: ir.Type
metadata: Any = None
def __iter__(self):
yield self.var
yield self.ir_type
yield self.metadata
def get_probe_string(func_node): def get_probe_string(func_node):
@ -61,12 +47,13 @@ def handle_assign(
if isinstance(target, ast.Attribute): if isinstance(target, ast.Attribute):
# struct field assignment # struct field assignment
field_name = target.attr field_name = target.attr
if var_name in local_sym_tab: if var_name in local_sym_tab and var_name in local_var_metadata:
struct_type = local_sym_tab[var_name].metadata struct_type = local_var_metadata[var_name]
struct_info = structs_sym_tab[struct_type] struct_info = structs_sym_tab[struct_type]
if field_name in struct_info.fields: if field_name in struct_info.fields:
field_ptr = struct_info.gep( field_ptr = struct_info.gep(
builder, local_sym_tab[var_name].var, field_name builder, local_sym_tab[var_name][0], field_name
) )
val = eval_expr( val = eval_expr(
func, func,
@ -96,21 +83,18 @@ def handle_assign(
elif isinstance(rval, ast.Constant): elif isinstance(rval, ast.Constant):
if isinstance(rval.value, bool): if isinstance(rval.value, bool):
if rval.value: if rval.value:
builder.store( builder.store(ir.Constant(ir.IntType(1), 1), local_sym_tab[var_name][0])
ir.Constant(ir.IntType(1), 1), local_sym_tab[var_name].var
)
else: else:
builder.store( builder.store(ir.Constant(ir.IntType(1), 0), local_sym_tab[var_name][0])
ir.Constant(ir.IntType(1), 0), local_sym_tab[var_name].var
)
print(f"Assigned constant {rval.value} to {var_name}") print(f"Assigned constant {rval.value} to {var_name}")
elif isinstance(rval.value, int): elif isinstance(rval.value, int):
# Assume c_int64 for now # Assume c_int64 for now
# var = builder.alloca(ir.IntType(64), name=var_name) # var = builder.alloca(ir.IntType(64), name=var_name)
# var.align = 8 # var.align = 8
builder.store( builder.store(
ir.Constant(ir.IntType(64), rval.value), local_sym_tab[var_name].var ir.Constant(ir.IntType(64), rval.value), local_sym_tab[var_name][0]
) )
# local_sym_tab[var_name] = var
print(f"Assigned constant {rval.value} to {var_name}") print(f"Assigned constant {rval.value} to {var_name}")
elif isinstance(rval.value, str): elif isinstance(rval.value, str):
str_val = rval.value.encode("utf-8") + b"\x00" str_val = rval.value.encode("utf-8") + b"\x00"
@ -124,7 +108,7 @@ def handle_assign(
global_str.global_constant = True global_str.global_constant = True
global_str.initializer = str_const global_str.initializer = str_const
str_ptr = builder.bitcast(global_str, ir.PointerType(ir.IntType(8))) str_ptr = builder.bitcast(global_str, ir.PointerType(ir.IntType(8)))
builder.store(str_ptr, local_sym_tab[var_name].var) builder.store(str_ptr, local_sym_tab[var_name][0])
print(f"Assigned string constant '{rval.value}' to {var_name}") print(f"Assigned string constant '{rval.value}' to {var_name}")
else: else:
print("Unsupported constant type") print("Unsupported constant type")
@ -142,13 +126,13 @@ def handle_assign(
# var = builder.alloca(ir_type, name=var_name) # var = builder.alloca(ir_type, name=var_name)
# var.align = ir_type.width // 8 # var.align = ir_type.width // 8
builder.store( builder.store(
ir.Constant(ir_type, rval.args[0].value), ir.Constant(ir_type, rval.args[0].value), local_sym_tab[var_name][0]
local_sym_tab[var_name].var,
) )
print( print(
f"Assigned {call_type} constant " f"Assigned {call_type} constant "
f"{rval.args[0].value} to {var_name}" f"{rval.args[0].value} to {var_name}"
) )
# local_sym_tab[var_name] = var
elif HelperHandlerRegistry.has_handler(call_type): elif HelperHandlerRegistry.has_handler(call_type):
# var = builder.alloca(ir.IntType(64), name=var_name) # var = builder.alloca(ir.IntType(64), name=var_name)
# var.align = 8 # var.align = 8
@ -160,8 +144,10 @@ def handle_assign(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_sym_tab, structs_sym_tab,
local_var_metadata,
) )
builder.store(val[0], local_sym_tab[var_name].var) builder.store(val[0], local_sym_tab[var_name][0])
# local_sym_tab[var_name] = var
print(f"Assigned constant {rval.func.id} to {var_name}") print(f"Assigned constant {rval.func.id} to {var_name}")
elif call_type == "deref" and len(rval.args) == 1: elif call_type == "deref" and len(rval.args) == 1:
print(f"Handling deref assignment {ast.dump(rval)}") print(f"Handling deref assignment {ast.dump(rval)}")
@ -178,15 +164,18 @@ def handle_assign(
print("Failed to evaluate deref argument") print("Failed to evaluate deref argument")
return return
print(f"Dereferenced value: {val}, storing in {var_name}") print(f"Dereferenced value: {val}, storing in {var_name}")
builder.store(val[0], local_sym_tab[var_name].var) builder.store(val[0], local_sym_tab[var_name][0])
# local_sym_tab[var_name] = var
print(f"Dereferenced and assigned to {var_name}") print(f"Dereferenced and assigned to {var_name}")
elif call_type in structs_sym_tab and len(rval.args) == 0: elif call_type in structs_sym_tab and len(rval.args) == 0:
struct_info = structs_sym_tab[call_type] struct_info = structs_sym_tab[call_type]
ir_type = struct_info.ir_type ir_type = struct_info.ir_type
# var = builder.alloca(ir_type, name=var_name) # var = builder.alloca(ir_type, name=var_name)
# Null init # Null init
builder.store(ir.Constant(ir_type, None), local_sym_tab[var_name].var) builder.store(ir.Constant(ir_type, None), local_sym_tab[var_name][0])
local_var_metadata[var_name] = call_type
print(f"Assigned struct {call_type} to {var_name}") print(f"Assigned struct {call_type} to {var_name}")
# local_sym_tab[var_name] = var
else: else:
print(f"Unsupported assignment call type: {call_type}") print(f"Unsupported assignment call type: {call_type}")
elif isinstance(rval.func, ast.Attribute): elif isinstance(rval.func, ast.Attribute):
@ -209,10 +198,12 @@ def handle_assign(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_sym_tab, structs_sym_tab,
local_var_metadata,
) )
# var = builder.alloca(ir.IntType(64), name=var_name) # var = builder.alloca(ir.IntType(64), name=var_name)
# var.align = 8 # var.align = 8
builder.store(val[0], local_sym_tab[var_name].var) builder.store(val[0], local_sym_tab[var_name][0])
# local_sym_tab[var_name] = var
else: else:
print("Unsupported assignment call structure") print("Unsupported assignment call structure")
else: else:
@ -236,7 +227,7 @@ def handle_cond(func, module, builder, cond, local_sym_tab, map_sym_tab):
return None return None
elif isinstance(cond, ast.Name): elif isinstance(cond, ast.Name):
if cond.id in local_sym_tab: if cond.id in local_sym_tab:
var = local_sym_tab[cond.id].var var = local_sym_tab[cond.id][0]
val = builder.load(var) val = builder.load(var)
if val.type != ir.IntType(1): if val.type != ir.IntType(1):
# Convert nonzero values to true, zero to false # Convert nonzero values to true, zero to false
@ -351,6 +342,7 @@ def process_stmt(
): ):
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( handle_expr(
func, func,
module, module,
@ -359,6 +351,7 @@ def process_stmt(
local_sym_tab, local_sym_tab,
map_sym_tab, map_sym_tab,
structs_sym_tab, structs_sym_tab,
local_var_metadata,
) )
elif isinstance(stmt, ast.Assign): elif isinstance(stmt, ast.Assign):
handle_assign( handle_assign(
@ -408,7 +401,6 @@ def allocate_mem(
module, builder, body, func, ret_type, map_sym_tab, local_sym_tab, structs_sym_tab module, builder, body, func, ret_type, map_sym_tab, local_sym_tab, structs_sym_tab
): ):
for stmt in body: for stmt in body:
has_metadata = False
if isinstance(stmt, ast.If): if isinstance(stmt, ast.If):
if stmt.body: if stmt.body:
local_sym_tab = allocate_mem( local_sym_tab = allocate_mem(
@ -466,7 +458,7 @@ def allocate_mem(
struct_info = structs_sym_tab[call_type] struct_info = structs_sym_tab[call_type]
ir_type = struct_info.ir_type ir_type = struct_info.ir_type
var = builder.alloca(ir_type, name=var_name) var = builder.alloca(ir_type, name=var_name)
has_metadata = True local_var_metadata[var_name] = call_type
print( print(
f"Pre-allocated variable {var_name} " f"Pre-allocated variable {var_name} "
f"for struct {call_type}" f"for struct {call_type}"
@ -508,11 +500,7 @@ def allocate_mem(
else: else:
print("Unsupported assignment value type") print("Unsupported assignment value type")
continue continue
local_sym_tab[var_name] = (var, ir_type)
if has_metadata:
local_sym_tab[var_name] = LocalSymbol(var, ir_type, call_type)
else:
local_sym_tab[var_name] = LocalSymbol(var, ir_type)
return local_sym_tab return local_sym_tab
@ -674,7 +662,7 @@ def infer_return_type(func_node: ast.FunctionDef):
if found_type is None: if found_type is None:
found_type = t found_type = t
elif found_type != t: elif found_type != t:
raise ValueError(f"Conflicting return types: {found_type} vs {t}") raise ValueError("Conflicting return types:" f"{found_type} vs {t}")
return found_type or "None" return found_type or "None"

View File

@ -19,6 +19,8 @@ class BPFHelperID(Enum):
BPF_PRINTK = 6 BPF_PRINTK = 6
BPF_GET_CURRENT_PID_TGID = 14 BPF_GET_CURRENT_PID_TGID = 14
BPF_PERF_EVENT_OUTPUT = 25 BPF_PERF_EVENT_OUTPUT = 25
BPF_RINGBUF_RESERVE = 131
BPF_RINGBUF_SUBMIT = 132
@HelperHandlerRegistry.register("ktime") @HelperHandlerRegistry.register("ktime")
@ -30,6 +32,7 @@ def bpf_ktime_get_ns_emitter(
func, func,
local_sym_tab=None, local_sym_tab=None,
struct_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.
@ -52,6 +55,7 @@ def bpf_map_lookup_elem_emitter(
func, func,
local_sym_tab=None, local_sym_tab=None,
struct_sym_tab=None, struct_sym_tab=None,
local_var_metadata=None,
): ):
""" """
Emit LLVM IR for bpf_map_lookup_elem helper function call. Emit LLVM IR for bpf_map_lookup_elem helper function call.
@ -87,6 +91,7 @@ def bpf_printk_emitter(
func, func,
local_sym_tab=None, local_sym_tab=None,
struct_sym_tab=None, struct_sym_tab=None,
local_var_metadata=None,
): ):
"""Emit LLVM IR for bpf_printk helper function call.""" """Emit LLVM IR for bpf_printk helper function call."""
if not hasattr(func, "_fmt_counter"): if not hasattr(func, "_fmt_counter"):
@ -104,6 +109,7 @@ def bpf_printk_emitter(
func, func,
local_sym_tab, local_sym_tab,
struct_sym_tab, struct_sym_tab,
local_var_metadata,
) )
elif isinstance(call.args[0], ast.Constant) and isinstance(call.args[0].value, str): elif isinstance(call.args[0], ast.Constant) and isinstance(call.args[0].value, str):
# TODO: We are only supporting single arguments for now. # TODO: We are only supporting single arguments for now.
@ -134,6 +140,7 @@ def bpf_map_update_elem_emitter(
func, func,
local_sym_tab=None, local_sym_tab=None,
struct_sym_tab=None, struct_sym_tab=None,
local_var_metadata=None,
): ):
""" """
Emit LLVM IR for bpf_map_update_elem helper function call. Emit LLVM IR for bpf_map_update_elem helper function call.
@ -175,6 +182,114 @@ def bpf_map_update_elem_emitter(
return result, None return result, None
@HelperHandlerRegistry.register("submit")
def bpf_ringbuf_submit_emitter(
call,
map_ptr,
module,
builder,
func,
local_sym_tab=None,
struct_sym_tab=None,
local_var_metadata=None,
):
"""
Emit LLVM IR for bpf_ringbuf_submit helper function call.
Expected call signature: ringbuf.submit(data, flags=0)
"""
if not call.args or len(call.args) < 1 or len(call.args) > 2:
raise ValueError(
"Ringbuf submit expects 1 or 2 args (data, flags), "
f"got {len(call.args)}"
)
data_arg = call.args[0]
data_ptr = get_or_create_ptr_from_arg(data_arg, builder, local_sym_tab)
# Get flags argument (default to 0)
flags_arg = call.args[1] if len(call.args) > 1 else None
flags_val = get_flags_val(flags_arg, builder, local_sym_tab)
# Returns: void
# Args: (void* data, u64 flags)
fn_type = ir.FunctionType(
ir.VoidType(),
[ir.PointerType(), ir.IntType(64)],
var_arg=False,
)
fn_ptr_type = ir.PointerType(fn_type)
fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_RINGBUF_SUBMIT.value)
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
if isinstance(flags_val, int):
flags_const = ir.Constant(ir.IntType(64), flags_val)
else:
flags_const = flags_val
builder.call(fn_ptr, [data_ptr, flags_const], tail=True)
return None
@HelperHandlerRegistry.register("reserve")
def bpf_ringbuf_reserve_emitter(
call,
map_ptr,
module,
builder,
func,
local_sym_tab=None,
struct_sym_tab=None,
local_var_metadata=None,
):
"""
Emit LLVM IR for bpf_ringbuf_reserve helper function call.
Expected call signature: ringbuf.reserve(size, flags=0)
"""
if not call.args or len(call.args) < 1 or len(call.args) > 2:
raise ValueError(
"Ringbuf reserve expects 1 or 2 args (size, flags), "
f"got {len(call.args)}"
)
# TODO: here, getting length of stuff does not actually work. need to fix this.
size_arg = call.args[0]
if isinstance(size_arg, ast.Constant):
size_val = ir.Constant(ir.IntType(64), size_arg.value)
elif isinstance(size_arg, ast.Name):
if size_arg.id not in local_sym_tab:
raise ValueError(
f"Variable '{size_arg.id}' not found in local symbol table"
)
size_val = builder.load(local_sym_tab[size_arg.id])
else:
raise NotImplementedError(f"Unsupported size argument type: {type(size_arg)}")
flags_arg = call.args[1] if len(call.args) > 1 else None
flags_val = get_flags_val(flags_arg, builder, local_sym_tab)
map_void_ptr = builder.bitcast(map_ptr, ir.PointerType())
# Args: (void* ringbuf, u64 size, u64 flags)
fn_type = ir.FunctionType(
ir.PointerType(),
[ir.PointerType(), ir.IntType(64), ir.IntType(64)],
var_arg=False,
)
fn_ptr_type = ir.PointerType(fn_type)
fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_RINGBUF_RESERVE.value)
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
if isinstance(flags_val, int):
flags_const = ir.Constant(ir.IntType(64), flags_val)
else:
flags_const = flags_val
result = builder.call(fn_ptr, [map_void_ptr, size_val, flags_const], tail=True)
return result, ir.PointerType()
@HelperHandlerRegistry.register("delete") @HelperHandlerRegistry.register("delete")
def bpf_map_delete_elem_emitter( def bpf_map_delete_elem_emitter(
@ -185,6 +300,7 @@ def bpf_map_delete_elem_emitter(
func, func,
local_sym_tab=None, local_sym_tab=None,
struct_sym_tab=None, struct_sym_tab=None,
local_var_metadata=None,
): ):
""" """
Emit LLVM IR for bpf_map_delete_elem helper function call. Emit LLVM IR for bpf_map_delete_elem helper function call.
@ -222,6 +338,7 @@ def bpf_get_current_pid_tgid_emitter(
func, func,
local_sym_tab=None, local_sym_tab=None,
struct_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.
@ -248,6 +365,7 @@ def bpf_perf_event_output_handler(
func, func,
local_sym_tab=None, local_sym_tab=None,
struct_sym_tab=None, struct_sym_tab=None,
local_var_metadata=None,
): ):
if len(call.args) != 1: if len(call.args) != 1:
raise ValueError( raise ValueError(
@ -256,7 +374,9 @@ def bpf_perf_event_output_handler(
data_arg = call.args[0] data_arg = call.args[0]
ctx_ptr = func.args[0] # First argument to the function is ctx ctx_ptr = func.args[0] # First argument to the function is ctx
data_ptr, size_val = get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab) data_ptr, size_val = get_data_ptr_and_size(
data_arg, local_sym_tab, struct_sym_tab, local_var_metadata
)
# BPF_F_CURRENT_CPU is -1 in 32 bit # BPF_F_CURRENT_CPU is -1 in 32 bit
flags_val = ir.Constant(ir.IntType(64), 0xFFFFFFFF) flags_val = ir.Constant(ir.IntType(64), 0xFFFFFFFF)
@ -294,6 +414,7 @@ def handle_helper_call(
local_sym_tab=None, local_sym_tab=None,
map_sym_tab=None, map_sym_tab=None,
struct_sym_tab=None, struct_sym_tab=None,
local_var_metadata=None,
): ):
"""Process a BPF helper function call and emit the appropriate LLVM IR.""" """Process a BPF helper function call and emit the appropriate LLVM IR."""
@ -312,6 +433,7 @@ def handle_helper_call(
func, func,
local_sym_tab, local_sym_tab,
struct_sym_tab, struct_sym_tab,
local_var_metadata,
) )
# Handle direct function calls (e.g., print(), ktime()) # Handle direct function calls (e.g., print(), ktime())

View File

@ -37,7 +37,7 @@ class HelperHandlerRegistry:
def get_var_ptr_from_name(var_name, local_sym_tab): def get_var_ptr_from_name(var_name, local_sym_tab):
"""Get a pointer to a variable from the symbol table.""" """Get a pointer to a variable from the symbol table."""
if local_sym_tab and var_name in local_sym_tab: if local_sym_tab and var_name in local_sym_tab:
return local_sym_tab[var_name].var return local_sym_tab[var_name][0]
raise ValueError(f"Variable '{var_name}' not found in local symbol table") raise ValueError(f"Variable '{var_name}' not found in local symbol table")
@ -72,7 +72,7 @@ def get_flags_val(arg, builder, local_sym_tab):
if isinstance(arg, ast.Name): if isinstance(arg, ast.Name):
if local_sym_tab and arg.id in local_sym_tab: if local_sym_tab and arg.id in local_sym_tab:
flags_ptr = local_sym_tab[arg.id].var flags_ptr = local_sym_tab[arg.id][0]
return builder.load(flags_ptr) return builder.load(flags_ptr)
else: else:
raise ValueError(f"Variable '{arg.id}' not found in local symbol table") raise ValueError(f"Variable '{arg.id}' not found in local symbol table")
@ -100,6 +100,7 @@ def handle_fstring_print(
func, func,
local_sym_tab=None, local_sym_tab=None,
struct_sym_tab=None, struct_sym_tab=None,
local_var_metadata=None,
): ):
"""Handle f-string formatting for bpf_printk emitter.""" """Handle f-string formatting for bpf_printk emitter."""
fmt_parts = [] fmt_parts = []
@ -117,6 +118,7 @@ def handle_fstring_print(
exprs, exprs,
local_sym_tab, local_sym_tab,
struct_sym_tab, struct_sym_tab,
local_var_metadata,
) )
else: else:
raise NotImplementedError(f"Unsupported f-string value type: {type(value)}") raise NotImplementedError(f"Unsupported f-string value type: {type(value)}")
@ -136,6 +138,7 @@ def handle_fstring_print(
builder, builder,
local_sym_tab, local_sym_tab,
struct_sym_tab, struct_sym_tab,
local_var_metadata,
) )
args.append(arg_value) args.append(arg_value)
@ -155,7 +158,9 @@ def _process_constant_in_fstring(cst, fmt_parts, exprs):
) )
def _process_fval(fval, fmt_parts, exprs, local_sym_tab, struct_sym_tab): def _process_fval(
fval, fmt_parts, exprs, local_sym_tab, struct_sym_tab, local_var_metadata
):
"""Process formatted values in f-string.""" """Process formatted values in f-string."""
logger.debug(f"Processing formatted value: {ast.dump(fval)}") logger.debug(f"Processing formatted value: {ast.dump(fval)}")
@ -168,6 +173,7 @@ def _process_fval(fval, fmt_parts, exprs, local_sym_tab, struct_sym_tab):
exprs, exprs,
local_sym_tab, local_sym_tab,
struct_sym_tab, struct_sym_tab,
local_var_metadata,
) )
else: else:
raise NotImplementedError( raise NotImplementedError(
@ -178,11 +184,13 @@ def _process_fval(fval, fmt_parts, exprs, local_sym_tab, struct_sym_tab):
def _process_name_in_fval(name_node, fmt_parts, exprs, local_sym_tab): def _process_name_in_fval(name_node, fmt_parts, exprs, local_sym_tab):
"""Process name nodes in formatted values.""" """Process name nodes in formatted values."""
if local_sym_tab and name_node.id in local_sym_tab: if local_sym_tab and name_node.id in local_sym_tab:
_, var_type, tmp = local_sym_tab[name_node.id] _, var_type = local_sym_tab[name_node.id]
_populate_fval(var_type, name_node, fmt_parts, exprs) _populate_fval(var_type, name_node, fmt_parts, exprs)
def _process_attr_in_fval(attr_node, fmt_parts, exprs, local_sym_tab, struct_sym_tab): def _process_attr_in_fval(
attr_node, fmt_parts, exprs, local_sym_tab, struct_sym_tab, local_var_metadata
):
"""Process attribute nodes in formatted values.""" """Process attribute nodes in formatted values."""
if ( if (
isinstance(attr_node.value, ast.Name) isinstance(attr_node.value, ast.Name)
@ -192,7 +200,12 @@ def _process_attr_in_fval(attr_node, fmt_parts, exprs, local_sym_tab, struct_sym
var_name = attr_node.value.id var_name = attr_node.value.id
field_name = attr_node.attr field_name = attr_node.attr
var_type = local_sym_tab[var_name].metadata if not local_var_metadata or var_name not in local_var_metadata:
raise ValueError(
f"Metadata for '{var_name}' not found in local var metadata"
)
var_type = local_var_metadata[var_name]
if var_type not in struct_sym_tab: if var_type not in struct_sym_tab:
raise ValueError( raise ValueError(
f"Struct '{var_type}' for '{var_name}' not in symbol table" f"Struct '{var_type}' for '{var_name}' not in symbol table"
@ -250,7 +263,9 @@ def _create_format_string_global(fmt_str, func, module, builder):
return builder.bitcast(fmt_gvar, ir.PointerType()) return builder.bitcast(fmt_gvar, ir.PointerType())
def _prepare_expr_args(expr, func, module, builder, local_sym_tab, struct_sym_tab): def _prepare_expr_args(
expr, func, module, builder, local_sym_tab, struct_sym_tab, local_var_metadata
):
"""Evaluate and prepare an expression to use as an arg for bpf_printk.""" """Evaluate and prepare an expression to use as an arg for bpf_printk."""
val, _ = eval_expr( val, _ = eval_expr(
func, func,
@ -260,6 +275,7 @@ def _prepare_expr_args(expr, func, module, builder, local_sym_tab, struct_sym_ta
local_sym_tab, local_sym_tab,
None, None,
struct_sym_tab, struct_sym_tab,
local_var_metadata,
) )
if val: if val:
@ -282,26 +298,34 @@ def _prepare_expr_args(expr, func, module, builder, local_sym_tab, struct_sym_ta
return ir.Constant(ir.IntType(64), 0) return ir.Constant(ir.IntType(64), 0)
def get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab): def get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab, local_var_metadata):
"""Extract data pointer and size information for perf event output.""" """Extract data pointer and size information for perf event output."""
if isinstance(data_arg, ast.Name): if isinstance(data_arg, ast.Name):
data_name = data_arg.id data_name = data_arg.id
if local_sym_tab and data_name in local_sym_tab: if local_sym_tab and data_name in local_sym_tab:
data_ptr = local_sym_tab[data_name].var data_ptr = local_sym_tab[data_name][0]
else: else:
raise ValueError( raise ValueError(
f"Data variable {data_name} not found in local symbol table." f"Data variable {data_name} not found in local symbol table."
) )
# Check if data_name is a struct # Check if data_name is a struct
data_type = local_sym_tab[data_name].metadata if local_var_metadata and data_name in local_var_metadata:
data_type = local_var_metadata[data_name]
if data_type in struct_sym_tab: if data_type in struct_sym_tab:
struct_info = struct_sym_tab[data_type] struct_info = struct_sym_tab[data_type]
size_val = ir.Constant(ir.IntType(64), struct_info.size) size_val = ir.Constant(ir.IntType(64), struct_info.size)
return data_ptr, size_val return data_ptr, size_val
else: else:
raise ValueError(f"Struct {data_type} for {data_name} not in symbol table.") raise ValueError(
f"Struct {data_type} for {data_name} not in symbol table."
)
else:
raise ValueError(
f"Metadata for variable {data_name} "
"not found in local variable metadata."
)
else: else:
raise NotImplementedError( raise NotImplementedError(
"Only simple object names are supported as data in perf event output." "Only simple object names are supported " "as data in perf event output."
) )

View File

@ -85,7 +85,7 @@ def create_bpf_map(module, map_name, map_params):
def create_map_debug_info(module, map_global, map_name, map_params): def create_map_debug_info(module, map_global, map_name, map_params):
"""Generate debug info metadata for BPF maps HASH and PERF_EVENT_ARRAY""" """Generate debug information metadata for BPF maps HASH and PERF_EVENT_ARRAY"""
generator = DebugInfoGenerator(module) generator = DebugInfoGenerator(module)
uint_type = generator.get_uint32_type() uint_type = generator.get_uint32_type()
@ -158,8 +158,7 @@ def create_ringbuf_debug_info(module, map_global, map_name, map_params):
type_ptr = generator.create_pointer_type(type_array, 64) type_ptr = generator.create_pointer_type(type_array, 64)
type_member = generator.create_struct_member("type", type_ptr, 0) type_member = generator.create_struct_member("type", type_ptr, 0)
max_entries_array = generator.create_array_type( max_entries_array = generator.create_array_type(int_type, map_params["max_entries"])
int_type, map_params["max_entries"])
max_entries_ptr = generator.create_pointer_type(max_entries_array, 64) max_entries_ptr = generator.create_pointer_type(max_entries_array, 64)
max_entries_member = generator.create_struct_member( max_entries_member = generator.create_struct_member(
"max_entries", max_entries_ptr, 64 "max_entries", max_entries_ptr, 64
@ -167,8 +166,7 @@ def create_ringbuf_debug_info(module, map_global, map_name, map_params):
elements_arr = [type_member, max_entries_member] elements_arr = [type_member, max_entries_member]
struct_type = generator.create_struct_type( struct_type = generator.create_struct_type(elements_arr, 128, is_distinct=True)
elements_arr, 128, is_distinct=True)
global_var = generator.create_global_var_debug_info( global_var = generator.create_global_var_debug_info(
map_name, struct_type, is_local=False map_name, struct_type, is_local=False

View File

@ -22,27 +22,29 @@ struct {
SEC("tracepoint/syscalls/sys_enter_execve") SEC("tracepoint/syscalls/sys_enter_execve")
int trace_execve(void *ctx) int trace_execve(void *ctx)
{ {
struct event *e; // struct event *e;
__u64 pid_tgid; // __u64 pid_tgid;
__u64 uid_gid; // __u64 uid_gid;
__u32 *e;
// Reserve space in the ringbuffer // Reserve space in the ringbuffer
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e) if (!e)
return 0; return 0;
//
// // Fill the struct with data
// pid_tgid = bpf_get_current_pid_tgid();
// e->pid = pid_tgid >> 32;
//
// uid_gid = bpf_get_current_uid_gid();
// e->uid = uid_gid & 0xFFFFFFFF;
//
// e->timestamp = bpf_ktime_get_ns();
// Fill the struct with data // bpf_get_current_comm(&e->comm, sizeof(e->comm));
pid_tgid = bpf_get_current_pid_tgid(); //
e->pid = pid_tgid >> 32; // // Submit the event to ringbuffer
__u32 temp = 32;
uid_gid = bpf_get_current_uid_gid(); e = &temp;
e->uid = uid_gid & 0xFFFFFFFF;
e->timestamp = bpf_ktime_get_ns();
bpf_get_current_comm(&e->comm, sizeof(e->comm));
// Submit the event to ringbuffer
bpf_ringbuf_submit(e, 0); bpf_ringbuf_submit(e, 0);
return 0; return 0;

View File

@ -0,0 +1,33 @@
from pythonbpf import bpf, map, bpfglobal, section, compile, compile_to_ir, BPF
from pythonbpf.maps import RingBuf
from ctypes import c_int32, c_void_p
# Define a map
@bpf
@map
def mymap() -> RingBuf:
return RingBuf(max_entries=(1024))
@bpf
@section("tracepoint/syscalls/sys_enter_clone")
def random_section(ctx: c_void_p) -> c_int32:
e: c_int32 = mymap().reserve(64)
if e == 0: # here is the issue i think
return c_int32(0)
mymap().submit(e)
return c_int32(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile_to_ir("ringbuf.py", "ringbuf.ll")
compile()
b = BPF()
b.load_and_attach()
while True:
print("running")

View File

@ -1,5 +1,5 @@
from pythonbpf import bpf, BPF, map, bpfglobal, section, compile, compile_to_ir from pythonbpf import bpf, map, bpfglobal, section, compile, compile_to_ir, BPF
from pythonbpf.maps import RingBuf, HashMap from pythonbpf.maps import RingBuf
from ctypes import c_int32, c_void_p from ctypes import c_int32, c_void_p
@ -9,17 +9,13 @@ from ctypes import c_int32, c_void_p
def mymap() -> RingBuf: def mymap() -> RingBuf:
return RingBuf(max_entries=(1024)) return RingBuf(max_entries=(1024))
@bpf
@map
def mymap2() -> HashMap:
return HashMap(key=c_int32, value=c_int32, max_entries=1024)
@bpf @bpf
@section("tracepoint/syscalls/sys_enter_clone") @section("tracepoint/syscalls/sys_enter_clone")
def random_section(ctx: c_void_p) -> c_int32: def random_section(ctx: c_void_p) -> c_int32:
print("Hello") print("Hello")
e = mymap().reserve(6)
if e:
mymap().submit(e)
return c_int32(0) return c_int32(0)
@ -33,3 +29,5 @@ compile_to_ir("ringbuf.py", "ringbuf.ll")
compile() compile()
b = BPF() b = BPF()
b.load_and_attach() b.load_and_attach()
while True:
print("running")