mirror of
https://github.com/varun-r-mallya/Python-BPF.git
synced 2025-12-31 21:06:25 +00:00
Compare commits
68 Commits
552cd352f2
...
refactor_c
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fdc6fa3ed | |||
| 17004d58df | |||
| 6362a5e665 | |||
| d38d73d5c6 | |||
| 0a6571726a | |||
| e62557bd1d | |||
| ee90ee9392 | |||
| 5f9eaff59c | |||
| b86341ce7a | |||
| 4857739eec | |||
| 3bb4b099c1 | |||
| e7912a088f | |||
| 95d63d969e | |||
| 1f96bab944 | |||
| f98491f3bd | |||
| 98f262ae22 | |||
| d2ff53052c | |||
| ecac24c1d2 | |||
| a764b095f8 | |||
| 95a196a91f | |||
| 6b59980874 | |||
| 3f9604a370 | |||
| 480afd1341 | |||
| ab71275566 | |||
| 2d850f457f | |||
| 9e1142bf05 | |||
| 1843ca6c53 | |||
| caa5d92c32 | |||
| f41693bc6d | |||
| b7092fa362 | |||
| 0e7dcafbab | |||
| a574527891 | |||
| 176673017c | |||
| 1d6226d829 | |||
| 12b712c217 | |||
| 2de280915a | |||
| 1cce49f5e0 | |||
| 682a7e6566 | |||
| fb63dbd698 | |||
| 4f433d00cc | |||
| 6cf5115ea9 | |||
| f11a43010d | |||
| d1055e4d41 | |||
| 28ce14ce34 | |||
| 5066cd4cfe | |||
| 0bfb3855b6 | |||
| 2f0dd20f1e | |||
| abef68c274 | |||
| 9aff614ff5 | |||
| 7b0e8a2fca | |||
| 3e68d6df4f | |||
| b75dc82f90 | |||
| f53ca3bd5b | |||
| 02885af1ca | |||
| e6e2a69506 | |||
| e4e92710c0 | |||
| f08bc9976c | |||
| 23183da2e1 | |||
| c6fef1693e | |||
| 192e03aa98 | |||
| 6f02b61527 | |||
| a21ff5633c | |||
| f96a6b94dc | |||
| e9f3aa25d2 | |||
| d0a8e96b70 | |||
| b09dc815fc | |||
| ceaac78633 | |||
| dc7a127fa6 |
@ -1,7 +1,7 @@
|
||||
import ast
|
||||
from llvmlite import ir
|
||||
from .license_pass import license_processing
|
||||
from .functions_pass import func_proc
|
||||
from .functions import func_proc
|
||||
from .maps import maps_proc
|
||||
from .structs import structs_proc
|
||||
from .globals_pass import globals_processing
|
||||
|
||||
4
pythonbpf/expr/__init__.py
Normal file
4
pythonbpf/expr/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .expr_pass import eval_expr, handle_expr
|
||||
from .type_normalization import convert_to_bool
|
||||
|
||||
__all__ = ["eval_expr", "handle_expr", "convert_to_bool"]
|
||||
445
pythonbpf/expr/expr_pass.py
Normal file
445
pythonbpf/expr/expr_pass.py
Normal file
@ -0,0 +1,445 @@
|
||||
import ast
|
||||
from llvmlite import ir
|
||||
from logging import Logger
|
||||
import logging
|
||||
from typing import Dict
|
||||
|
||||
from pythonbpf.type_deducer import ctypes_to_ir, is_ctypes
|
||||
from .type_normalization import convert_to_bool, handle_comparator
|
||||
|
||||
logger: Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _handle_name_expr(expr: ast.Name, local_sym_tab: Dict, builder: ir.IRBuilder):
|
||||
"""Handle ast.Name expressions."""
|
||||
if expr.id in local_sym_tab:
|
||||
var = local_sym_tab[expr.id].var
|
||||
val = builder.load(var)
|
||||
return val, local_sym_tab[expr.id].ir_type
|
||||
else:
|
||||
logger.info(f"Undefined variable {expr.id}")
|
||||
return None
|
||||
|
||||
|
||||
def _handle_constant_expr(expr: ast.Constant):
|
||||
"""Handle ast.Constant expressions."""
|
||||
if isinstance(expr.value, int) or isinstance(expr.value, bool):
|
||||
return ir.Constant(ir.IntType(64), int(expr.value)), ir.IntType(64)
|
||||
else:
|
||||
logger.error("Unsupported constant type")
|
||||
return None
|
||||
|
||||
|
||||
def _handle_attribute_expr(
|
||||
expr: ast.Attribute,
|
||||
local_sym_tab: Dict,
|
||||
structs_sym_tab: Dict,
|
||||
builder: ir.IRBuilder,
|
||||
):
|
||||
"""Handle ast.Attribute expressions for struct field access."""
|
||||
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, var_metadata = local_sym_tab[var_name]
|
||||
logger.info(f"Loading attribute {attr_name} from variable {var_name}")
|
||||
logger.info(f"Variable type: {var_type}, Variable ptr: {var_ptr}")
|
||||
metadata = structs_sym_tab[var_metadata]
|
||||
if attr_name in metadata.fields:
|
||||
gep = metadata.gep(builder, var_ptr, attr_name)
|
||||
val = builder.load(gep)
|
||||
field_type = metadata.field_type(attr_name)
|
||||
return val, field_type
|
||||
return None
|
||||
|
||||
|
||||
def _handle_deref_call(expr: ast.Call, local_sym_tab: Dict, builder: ir.IRBuilder):
|
||||
"""Handle deref function calls."""
|
||||
logger.info(f"Handling deref {ast.dump(expr)}")
|
||||
if len(expr.args) != 1:
|
||||
logger.info("deref takes exactly one argument")
|
||||
return None
|
||||
|
||||
arg = expr.args[0]
|
||||
if (
|
||||
isinstance(arg, ast.Call)
|
||||
and isinstance(arg.func, ast.Name)
|
||||
and arg.func.id == "deref"
|
||||
):
|
||||
logger.info("Multiple deref not supported")
|
||||
return None
|
||||
|
||||
if isinstance(arg, ast.Name):
|
||||
if arg.id in local_sym_tab:
|
||||
arg_ptr = local_sym_tab[arg.id].var
|
||||
else:
|
||||
logger.info(f"Undefined variable {arg.id}")
|
||||
return None
|
||||
else:
|
||||
logger.info("Unsupported argument type for deref")
|
||||
return None
|
||||
|
||||
if arg_ptr is None:
|
||||
logger.info("Failed to evaluate deref argument")
|
||||
return None
|
||||
|
||||
# Load the value from pointer
|
||||
val = builder.load(arg_ptr)
|
||||
return val, local_sym_tab[arg.id].ir_type
|
||||
|
||||
|
||||
def _handle_ctypes_call(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
expr,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab=None,
|
||||
):
|
||||
"""Handle ctypes type constructor calls."""
|
||||
if len(expr.args) != 1:
|
||||
logger.info("ctypes constructor takes exactly one argument")
|
||||
return None
|
||||
|
||||
arg = expr.args[0]
|
||||
val = eval_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
arg,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
if val is None:
|
||||
logger.info("Failed to evaluate argument to ctypes constructor")
|
||||
return None
|
||||
call_type = expr.func.id
|
||||
expected_type = ctypes_to_ir(call_type)
|
||||
|
||||
if val[1] != expected_type:
|
||||
# NOTE: We are only considering casting to and from int types for now
|
||||
if isinstance(val[1], ir.IntType) and isinstance(expected_type, ir.IntType):
|
||||
if val[1].width < expected_type.width:
|
||||
val = (builder.sext(val[0], expected_type), expected_type)
|
||||
else:
|
||||
val = (builder.trunc(val[0], expected_type), expected_type)
|
||||
else:
|
||||
raise ValueError(f"Type mismatch: expected {expected_type}, got {val[1]}")
|
||||
return val
|
||||
|
||||
|
||||
def _handle_compare(
|
||||
func, module, builder, cond, local_sym_tab, map_sym_tab, structs_sym_tab=None
|
||||
):
|
||||
"""Handle ast.Compare expressions."""
|
||||
|
||||
if len(cond.ops) != 1 or len(cond.comparators) != 1:
|
||||
logger.error("Only single comparisons are supported")
|
||||
return None
|
||||
lhs = eval_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
cond.left,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
rhs = eval_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
cond.comparators[0],
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
|
||||
if lhs is None or rhs is None:
|
||||
logger.error("Failed to evaluate comparison operands")
|
||||
return None
|
||||
|
||||
lhs, _ = lhs
|
||||
rhs, _ = rhs
|
||||
return handle_comparator(func, builder, cond.ops[0], lhs, rhs)
|
||||
|
||||
|
||||
def _handle_unary_op(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
expr: ast.UnaryOp,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab=None,
|
||||
):
|
||||
"""Handle ast.UnaryOp expressions."""
|
||||
if not isinstance(expr.op, ast.Not):
|
||||
logger.error("Only 'not' unary operator is supported")
|
||||
return None
|
||||
|
||||
operand = eval_expr(
|
||||
func, module, builder, expr.operand, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
if operand is None:
|
||||
logger.error("Failed to evaluate operand for unary operation")
|
||||
return None
|
||||
|
||||
operand_val, operand_type = operand
|
||||
true_const = ir.Constant(ir.IntType(1), 1)
|
||||
result = builder.xor(convert_to_bool(builder, operand_val), true_const)
|
||||
return result, ir.IntType(1)
|
||||
|
||||
|
||||
def _handle_and_op(func, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab):
|
||||
"""Handle `and` boolean operations."""
|
||||
|
||||
logger.debug(f"Handling 'and' operator with {len(expr.values)} operands")
|
||||
|
||||
merge_block = func.append_basic_block(name="and.merge")
|
||||
false_block = func.append_basic_block(name="and.false")
|
||||
|
||||
incoming_values = []
|
||||
|
||||
for i, value in enumerate(expr.values):
|
||||
is_last = i == len(expr.values) - 1
|
||||
|
||||
# Evaluate current operand
|
||||
operand_result = eval_expr(
|
||||
func, None, builder, value, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
if operand_result is None:
|
||||
logger.error(f"Failed to evaluate operand {i} in 'and' expression")
|
||||
return None
|
||||
|
||||
operand_val, operand_type = operand_result
|
||||
|
||||
# Convert to boolean if needed
|
||||
operand_bool = convert_to_bool(builder, operand_val)
|
||||
current_block = builder.block
|
||||
|
||||
if is_last:
|
||||
# Last operand: result is this value
|
||||
builder.branch(merge_block)
|
||||
incoming_values.append((operand_bool, current_block))
|
||||
else:
|
||||
# Not last: check if true, continue or short-circuit
|
||||
next_check = func.append_basic_block(name=f"and.check_{i + 1}")
|
||||
builder.cbranch(operand_bool, next_check, false_block)
|
||||
builder.position_at_end(next_check)
|
||||
|
||||
# False block: short-circuit with false
|
||||
builder.position_at_end(false_block)
|
||||
builder.branch(merge_block)
|
||||
false_value = ir.Constant(ir.IntType(1), 0)
|
||||
incoming_values.append((false_value, false_block))
|
||||
|
||||
# Merge block: phi node
|
||||
builder.position_at_end(merge_block)
|
||||
phi = builder.phi(ir.IntType(1), name="and.result")
|
||||
for val, block in incoming_values:
|
||||
phi.add_incoming(val, block)
|
||||
|
||||
logger.debug(f"Generated 'and' with {len(incoming_values)} incoming values")
|
||||
return phi, ir.IntType(1)
|
||||
|
||||
|
||||
def _handle_or_op(func, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab):
|
||||
"""Handle `or` boolean operations."""
|
||||
|
||||
logger.debug(f"Handling 'or' operator with {len(expr.values)} operands")
|
||||
|
||||
merge_block = func.append_basic_block(name="or.merge")
|
||||
true_block = func.append_basic_block(name="or.true")
|
||||
|
||||
incoming_values = []
|
||||
|
||||
for i, value in enumerate(expr.values):
|
||||
is_last = i == len(expr.values) - 1
|
||||
|
||||
# Evaluate current operand
|
||||
operand_result = eval_expr(
|
||||
func, None, builder, value, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
if operand_result is None:
|
||||
logger.error(f"Failed to evaluate operand {i} in 'or' expression")
|
||||
return None
|
||||
|
||||
operand_val, operand_type = operand_result
|
||||
|
||||
# Convert to boolean if needed
|
||||
operand_bool = convert_to_bool(builder, operand_val)
|
||||
current_block = builder.block
|
||||
|
||||
if is_last:
|
||||
# Last operand: result is this value
|
||||
builder.branch(merge_block)
|
||||
incoming_values.append((operand_bool, current_block))
|
||||
else:
|
||||
# Not last: check if false, continue or short-circuit
|
||||
next_check = func.append_basic_block(name=f"or.check_{i + 1}")
|
||||
builder.cbranch(operand_bool, true_block, next_check)
|
||||
builder.position_at_end(next_check)
|
||||
|
||||
# True block: short-circuit with true
|
||||
builder.position_at_end(true_block)
|
||||
builder.branch(merge_block)
|
||||
true_value = ir.Constant(ir.IntType(1), 1)
|
||||
incoming_values.append((true_value, true_block))
|
||||
|
||||
# Merge block: phi node
|
||||
builder.position_at_end(merge_block)
|
||||
phi = builder.phi(ir.IntType(1), name="or.result")
|
||||
for val, block in incoming_values:
|
||||
phi.add_incoming(val, block)
|
||||
|
||||
logger.debug(f"Generated 'or' with {len(incoming_values)} incoming values")
|
||||
return phi, ir.IntType(1)
|
||||
|
||||
|
||||
def _handle_boolean_op(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
expr: ast.BoolOp,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab=None,
|
||||
):
|
||||
"""Handle `and` and `or` boolean operations."""
|
||||
|
||||
if isinstance(expr.op, ast.And):
|
||||
return _handle_and_op(
|
||||
func, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
elif isinstance(expr.op, ast.Or):
|
||||
return _handle_or_op(
|
||||
func, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
else:
|
||||
logger.error(f"Unsupported boolean operator: {type(expr.op).__name__}")
|
||||
return None
|
||||
|
||||
|
||||
def eval_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
expr,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab=None,
|
||||
):
|
||||
logger.info(f"Evaluating expression: {ast.dump(expr)}")
|
||||
if isinstance(expr, ast.Name):
|
||||
return _handle_name_expr(expr, local_sym_tab, builder)
|
||||
elif isinstance(expr, ast.Constant):
|
||||
return _handle_constant_expr(expr)
|
||||
elif isinstance(expr, ast.Call):
|
||||
if isinstance(expr.func, ast.Name) and expr.func.id == "deref":
|
||||
return _handle_deref_call(expr, local_sym_tab, builder)
|
||||
|
||||
if isinstance(expr.func, ast.Name) and is_ctypes(expr.func.id):
|
||||
return _handle_ctypes_call(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
expr,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
|
||||
# delayed import to avoid circular dependency
|
||||
from pythonbpf.helper import HelperHandlerRegistry, handle_helper_call
|
||||
|
||||
if isinstance(expr.func, ast.Name) and HelperHandlerRegistry.has_handler(
|
||||
expr.func.id
|
||||
):
|
||||
return handle_helper_call(
|
||||
expr,
|
||||
module,
|
||||
builder,
|
||||
func,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
elif isinstance(expr.func, ast.Attribute):
|
||||
logger.info(f"Handling method call: {ast.dump(expr.func)}")
|
||||
if isinstance(expr.func.value, ast.Call) and isinstance(
|
||||
expr.func.value.func, ast.Name
|
||||
):
|
||||
method_name = expr.func.attr
|
||||
if HelperHandlerRegistry.has_handler(method_name):
|
||||
return handle_helper_call(
|
||||
expr,
|
||||
module,
|
||||
builder,
|
||||
func,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
elif isinstance(expr.func.value, ast.Name):
|
||||
obj_name = expr.func.value.id
|
||||
method_name = expr.func.attr
|
||||
if obj_name in map_sym_tab:
|
||||
if HelperHandlerRegistry.has_handler(method_name):
|
||||
return handle_helper_call(
|
||||
expr,
|
||||
module,
|
||||
builder,
|
||||
func,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
elif isinstance(expr, ast.Attribute):
|
||||
return _handle_attribute_expr(expr, local_sym_tab, structs_sym_tab, builder)
|
||||
elif isinstance(expr, ast.BinOp):
|
||||
from pythonbpf.binary_ops import handle_binary_op
|
||||
|
||||
return handle_binary_op(expr, builder, None, local_sym_tab)
|
||||
elif isinstance(expr, ast.Compare):
|
||||
return _handle_compare(
|
||||
func, module, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
elif isinstance(expr, ast.UnaryOp):
|
||||
return _handle_unary_op(
|
||||
func, module, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
elif isinstance(expr, ast.BoolOp):
|
||||
return _handle_boolean_op(
|
||||
func, module, builder, expr, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
logger.info("Unsupported expression evaluation")
|
||||
return None
|
||||
|
||||
|
||||
def handle_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
expr,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
):
|
||||
"""Handle expression statements in the function body."""
|
||||
logger.info(f"Handling expression: {ast.dump(expr)}")
|
||||
call = expr.value
|
||||
if isinstance(call, ast.Call):
|
||||
eval_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
call,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
else:
|
||||
logger.info("Unsupported expression type")
|
||||
128
pythonbpf/expr/type_normalization.py
Normal file
128
pythonbpf/expr/type_normalization.py
Normal file
@ -0,0 +1,128 @@
|
||||
from llvmlite import ir
|
||||
import logging
|
||||
import ast
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
COMPARISON_OPS = {
|
||||
ast.Eq: "==",
|
||||
ast.NotEq: "!=",
|
||||
ast.Lt: "<",
|
||||
ast.LtE: "<=",
|
||||
ast.Gt: ">",
|
||||
ast.GtE: ">=",
|
||||
ast.Is: "==",
|
||||
ast.IsNot: "!=",
|
||||
}
|
||||
|
||||
|
||||
def _get_base_type_and_depth(ir_type):
|
||||
"""Get the base type for pointer types."""
|
||||
cur_type = ir_type
|
||||
depth = 0
|
||||
while isinstance(cur_type, ir.PointerType):
|
||||
depth += 1
|
||||
cur_type = cur_type.pointee
|
||||
return cur_type, depth
|
||||
|
||||
|
||||
def _deref_to_depth(func, builder, val, target_depth):
|
||||
"""Dereference a pointer to a certain depth."""
|
||||
|
||||
cur_val = val
|
||||
cur_type = val.type
|
||||
|
||||
for depth in range(target_depth):
|
||||
if not isinstance(val.type, ir.PointerType):
|
||||
logger.error("Cannot dereference further, non-pointer type")
|
||||
return None
|
||||
|
||||
# dereference with null check
|
||||
pointee_type = cur_type.pointee
|
||||
null_check_block = builder.block
|
||||
not_null_block = func.append_basic_block(name=f"deref_not_null_{depth}")
|
||||
merge_block = func.append_basic_block(name=f"deref_merge_{depth}")
|
||||
|
||||
null_ptr = ir.Constant(cur_type, None)
|
||||
is_not_null = builder.icmp_signed("!=", cur_val, null_ptr)
|
||||
logger.debug(f"Inserted null check for pointer at depth {depth}")
|
||||
|
||||
builder.cbranch(is_not_null, not_null_block, merge_block)
|
||||
|
||||
builder.position_at_end(not_null_block)
|
||||
dereferenced_val = builder.load(cur_val)
|
||||
logger.debug(f"Dereferenced to depth {depth - 1}, type: {pointee_type}")
|
||||
builder.branch(merge_block)
|
||||
|
||||
builder.position_at_end(merge_block)
|
||||
phi = builder.phi(pointee_type, name=f"deref_result_{depth}")
|
||||
|
||||
zero_value = (
|
||||
ir.Constant(pointee_type, 0)
|
||||
if isinstance(pointee_type, ir.IntType)
|
||||
else ir.Constant(pointee_type, None)
|
||||
)
|
||||
phi.add_incoming(zero_value, null_check_block)
|
||||
|
||||
phi.add_incoming(dereferenced_val, not_null_block)
|
||||
|
||||
# Continue with phi result
|
||||
cur_val = phi
|
||||
cur_type = pointee_type
|
||||
return cur_val
|
||||
|
||||
|
||||
def _normalize_types(func, builder, lhs, rhs):
|
||||
"""Normalize types for comparison."""
|
||||
|
||||
logger.info(f"Normalizing types: {lhs.type} vs {rhs.type}")
|
||||
if isinstance(lhs.type, ir.IntType) and isinstance(rhs.type, ir.IntType):
|
||||
if lhs.type.width < rhs.type.width:
|
||||
lhs = builder.sext(lhs, rhs.type)
|
||||
else:
|
||||
rhs = builder.sext(rhs, lhs.type)
|
||||
return lhs, rhs
|
||||
elif not isinstance(lhs.type, ir.PointerType) and not isinstance(
|
||||
rhs.type, ir.PointerType
|
||||
):
|
||||
logger.error(f"Type mismatch: {lhs.type} vs {rhs.type}")
|
||||
return None, None
|
||||
else:
|
||||
lhs_base, lhs_depth = _get_base_type_and_depth(lhs.type)
|
||||
rhs_base, rhs_depth = _get_base_type_and_depth(rhs.type)
|
||||
if lhs_base == rhs_base:
|
||||
if lhs_depth < rhs_depth:
|
||||
rhs = _deref_to_depth(func, builder, rhs, rhs_depth - lhs_depth)
|
||||
elif rhs_depth < lhs_depth:
|
||||
lhs = _deref_to_depth(func, builder, lhs, lhs_depth - rhs_depth)
|
||||
return _normalize_types(func, builder, lhs, rhs)
|
||||
|
||||
|
||||
def convert_to_bool(builder, val):
|
||||
"""Convert a value to boolean."""
|
||||
if val.type == ir.IntType(1):
|
||||
return val
|
||||
if isinstance(val.type, ir.PointerType):
|
||||
zero = ir.Constant(val.type, None)
|
||||
else:
|
||||
zero = ir.Constant(val.type, 0)
|
||||
return builder.icmp_signed("!=", val, zero)
|
||||
|
||||
|
||||
def handle_comparator(func, builder, op, lhs, rhs):
|
||||
"""Handle comparison operations."""
|
||||
|
||||
if lhs.type != rhs.type:
|
||||
lhs, rhs = _normalize_types(func, builder, lhs, rhs)
|
||||
|
||||
if lhs is None or rhs is None:
|
||||
return None
|
||||
|
||||
if type(op) not in COMPARISON_OPS:
|
||||
logger.error(f"Unsupported comparison operator: {type(op)}")
|
||||
return None
|
||||
|
||||
predicate = COMPARISON_OPS[type(op)]
|
||||
result = builder.icmp_signed(predicate, lhs, rhs)
|
||||
logger.debug(f"Comparison result: {result}")
|
||||
return result, ir.IntType(1)
|
||||
@ -1,183 +0,0 @@
|
||||
import ast
|
||||
from llvmlite import ir
|
||||
from logging import Logger
|
||||
import logging
|
||||
from typing import Dict
|
||||
|
||||
logger: Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _handle_name_expr(expr: ast.Name, local_sym_tab: Dict, builder: ir.IRBuilder):
|
||||
"""Handle ast.Name expressions."""
|
||||
if expr.id in local_sym_tab:
|
||||
var = local_sym_tab[expr.id].var
|
||||
val = builder.load(var)
|
||||
return val, local_sym_tab[expr.id].ir_type
|
||||
else:
|
||||
logger.info(f"Undefined variable {expr.id}")
|
||||
return None
|
||||
|
||||
|
||||
def _handle_constant_expr(expr: ast.Constant):
|
||||
"""Handle ast.Constant expressions."""
|
||||
if isinstance(expr.value, int):
|
||||
return ir.Constant(ir.IntType(64), expr.value), ir.IntType(64)
|
||||
elif isinstance(expr.value, bool):
|
||||
return ir.Constant(ir.IntType(1), int(expr.value)), ir.IntType(1)
|
||||
else:
|
||||
logger.info("Unsupported constant type")
|
||||
return None
|
||||
|
||||
|
||||
def _handle_attribute_expr(
|
||||
expr: ast.Attribute,
|
||||
local_sym_tab: Dict,
|
||||
structs_sym_tab: Dict,
|
||||
builder: ir.IRBuilder,
|
||||
):
|
||||
"""Handle ast.Attribute expressions for struct field access."""
|
||||
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, var_metadata = local_sym_tab[var_name]
|
||||
logger.info(f"Loading attribute {attr_name} from variable {var_name}")
|
||||
logger.info(f"Variable type: {var_type}, Variable ptr: {var_ptr}")
|
||||
|
||||
metadata = structs_sym_tab[var_metadata]
|
||||
if attr_name in metadata.fields:
|
||||
gep = metadata.gep(builder, var_ptr, attr_name)
|
||||
val = builder.load(gep)
|
||||
field_type = metadata.field_type(attr_name)
|
||||
return val, field_type
|
||||
return None
|
||||
|
||||
|
||||
def _handle_deref_call(expr: ast.Call, local_sym_tab: Dict, builder: ir.IRBuilder):
|
||||
"""Handle deref function calls."""
|
||||
logger.info(f"Handling deref {ast.dump(expr)}")
|
||||
if len(expr.args) != 1:
|
||||
logger.info("deref takes exactly one argument")
|
||||
return None
|
||||
|
||||
arg = expr.args[0]
|
||||
if (
|
||||
isinstance(arg, ast.Call)
|
||||
and isinstance(arg.func, ast.Name)
|
||||
and arg.func.id == "deref"
|
||||
):
|
||||
logger.info("Multiple deref not supported")
|
||||
return None
|
||||
|
||||
if isinstance(arg, ast.Name):
|
||||
if arg.id in local_sym_tab:
|
||||
arg_ptr = local_sym_tab[arg.id].var
|
||||
else:
|
||||
logger.info(f"Undefined variable {arg.id}")
|
||||
return None
|
||||
else:
|
||||
logger.info("Unsupported argument type for deref")
|
||||
return None
|
||||
|
||||
if arg_ptr is None:
|
||||
logger.info("Failed to evaluate deref argument")
|
||||
return None
|
||||
|
||||
# Load the value from pointer
|
||||
val = builder.load(arg_ptr)
|
||||
return val, local_sym_tab[arg.id].ir_type
|
||||
|
||||
|
||||
def eval_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
expr,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab=None,
|
||||
):
|
||||
logger.info(f"Evaluating expression: {ast.dump(expr)}")
|
||||
if isinstance(expr, ast.Name):
|
||||
return _handle_name_expr(expr, local_sym_tab, builder)
|
||||
elif isinstance(expr, ast.Constant):
|
||||
return _handle_constant_expr(expr)
|
||||
elif isinstance(expr, ast.Call):
|
||||
if isinstance(expr.func, ast.Name) and expr.func.id == "deref":
|
||||
return _handle_deref_call(expr, local_sym_tab, builder)
|
||||
|
||||
# delayed import to avoid circular dependency
|
||||
from pythonbpf.helper import HelperHandlerRegistry, handle_helper_call
|
||||
|
||||
if isinstance(expr.func, ast.Name) and HelperHandlerRegistry.has_handler(
|
||||
expr.func.id
|
||||
):
|
||||
return handle_helper_call(
|
||||
expr,
|
||||
module,
|
||||
builder,
|
||||
func,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
elif isinstance(expr.func, ast.Attribute):
|
||||
logger.info(f"Handling method call: {ast.dump(expr.func)}")
|
||||
if isinstance(expr.func.value, ast.Call) and isinstance(
|
||||
expr.func.value.func, ast.Name
|
||||
):
|
||||
method_name = expr.func.attr
|
||||
if HelperHandlerRegistry.has_handler(method_name):
|
||||
return handle_helper_call(
|
||||
expr,
|
||||
module,
|
||||
builder,
|
||||
func,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
elif isinstance(expr.func.value, ast.Name):
|
||||
obj_name = expr.func.value.id
|
||||
method_name = expr.func.attr
|
||||
if obj_name in map_sym_tab:
|
||||
if HelperHandlerRegistry.has_handler(method_name):
|
||||
return handle_helper_call(
|
||||
expr,
|
||||
module,
|
||||
builder,
|
||||
func,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
elif isinstance(expr, ast.Attribute):
|
||||
return _handle_attribute_expr(expr, local_sym_tab, structs_sym_tab, builder)
|
||||
logger.info("Unsupported expression evaluation")
|
||||
return None
|
||||
|
||||
|
||||
def handle_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
expr,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
):
|
||||
"""Handle expression statements in the function body."""
|
||||
logger.info(f"Handling expression: {ast.dump(expr)}")
|
||||
call = expr.value
|
||||
if isinstance(call, ast.Call):
|
||||
eval_expr(
|
||||
func,
|
||||
module,
|
||||
builder,
|
||||
call,
|
||||
local_sym_tab,
|
||||
map_sym_tab,
|
||||
structs_sym_tab,
|
||||
)
|
||||
else:
|
||||
logger.info("Unsupported expression type")
|
||||
3
pythonbpf/functions/__init__.py
Normal file
3
pythonbpf/functions/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .functions_pass import func_proc
|
||||
|
||||
__all__ = ["func_proc"]
|
||||
22
pythonbpf/functions/func_registry_handlers.py
Normal file
22
pythonbpf/functions/func_registry_handlers.py
Normal file
@ -0,0 +1,22 @@
|
||||
from typing import Dict
|
||||
|
||||
|
||||
class StatementHandlerRegistry:
|
||||
"""Registry for statement handlers."""
|
||||
|
||||
_handlers: Dict = {}
|
||||
|
||||
@classmethod
|
||||
def register(cls, stmt_type):
|
||||
"""Register a handler for a specific statement type."""
|
||||
|
||||
def decorator(handler):
|
||||
cls._handlers[stmt_type] = handler
|
||||
return handler
|
||||
|
||||
return decorator
|
||||
|
||||
@classmethod
|
||||
def __getitem__(cls, stmt_type):
|
||||
"""Get the handler for a specific statement type."""
|
||||
return cls._handlers.get(stmt_type, None)
|
||||
@ -4,10 +4,13 @@ import logging
|
||||
from typing import Any
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .helper import HelperHandlerRegistry, handle_helper_call
|
||||
from .type_deducer import ctypes_to_ir
|
||||
from .binary_ops import handle_binary_op
|
||||
from .expr_pass import eval_expr, handle_expr
|
||||
from pythonbpf.helper import HelperHandlerRegistry, handle_helper_call
|
||||
from pythonbpf.type_deducer import ctypes_to_ir
|
||||
from pythonbpf.binary_ops import handle_binary_op
|
||||
from pythonbpf.expr import eval_expr, handle_expr, convert_to_bool
|
||||
|
||||
from .return_utils import _handle_none_return, _handle_xdp_return, _is_xdp_name
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -237,71 +240,13 @@ def handle_assign(
|
||||
logger.info("Unsupported assignment value type")
|
||||
|
||||
|
||||
def handle_cond(func, module, builder, cond, local_sym_tab, map_sym_tab):
|
||||
if isinstance(cond, ast.Constant):
|
||||
if isinstance(cond.value, bool):
|
||||
return ir.Constant(ir.IntType(1), int(cond.value))
|
||||
elif isinstance(cond.value, int):
|
||||
return ir.Constant(ir.IntType(1), int(bool(cond.value)))
|
||||
else:
|
||||
logger.info("Unsupported constant type in condition")
|
||||
return None
|
||||
elif isinstance(cond, ast.Name):
|
||||
if cond.id in local_sym_tab:
|
||||
var = local_sym_tab[cond.id].var
|
||||
val = builder.load(var)
|
||||
if val.type != ir.IntType(1):
|
||||
# Convert nonzero values to true, zero to false
|
||||
if isinstance(val.type, ir.PointerType):
|
||||
# For pointer types, compare with null pointer
|
||||
zero = ir.Constant(val.type, None)
|
||||
else:
|
||||
# For integer types, compare with zero
|
||||
zero = ir.Constant(val.type, 0)
|
||||
val = builder.icmp_signed("!=", val, zero)
|
||||
return val
|
||||
else:
|
||||
logger.info(f"Undefined variable {cond.id} in condition")
|
||||
return None
|
||||
elif isinstance(cond, ast.Compare):
|
||||
lhs = eval_expr(func, module, builder, cond.left, local_sym_tab, map_sym_tab)[0]
|
||||
if len(cond.ops) != 1 or len(cond.comparators) != 1:
|
||||
logger.info("Unsupported complex comparison")
|
||||
return None
|
||||
rhs = eval_expr(
|
||||
func, module, builder, cond.comparators[0], local_sym_tab, map_sym_tab
|
||||
)[0]
|
||||
op = cond.ops[0]
|
||||
|
||||
if lhs.type != rhs.type:
|
||||
if isinstance(lhs.type, ir.IntType) and isinstance(rhs.type, ir.IntType):
|
||||
# Extend the smaller type to the larger type
|
||||
if lhs.type.width < rhs.type.width:
|
||||
lhs = builder.sext(lhs, rhs.type)
|
||||
elif lhs.type.width > rhs.type.width:
|
||||
rhs = builder.sext(rhs, lhs.type)
|
||||
else:
|
||||
logger.info("Type mismatch in comparison")
|
||||
return None
|
||||
|
||||
if isinstance(op, ast.Eq):
|
||||
return builder.icmp_signed("==", lhs, rhs)
|
||||
elif isinstance(op, ast.NotEq):
|
||||
return builder.icmp_signed("!=", lhs, rhs)
|
||||
elif isinstance(op, ast.Lt):
|
||||
return builder.icmp_signed("<", lhs, rhs)
|
||||
elif isinstance(op, ast.LtE):
|
||||
return builder.icmp_signed("<=", lhs, rhs)
|
||||
elif isinstance(op, ast.Gt):
|
||||
return builder.icmp_signed(">", lhs, rhs)
|
||||
elif isinstance(op, ast.GtE):
|
||||
return builder.icmp_signed(">=", lhs, rhs)
|
||||
else:
|
||||
logger.info("Unsupported comparison operator")
|
||||
return None
|
||||
else:
|
||||
logger.info("Unsupported condition expression")
|
||||
return None
|
||||
def handle_cond(
|
||||
func, module, builder, cond, local_sym_tab, map_sym_tab, structs_sym_tab=None
|
||||
):
|
||||
val = eval_expr(
|
||||
func, module, builder, cond, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)[0]
|
||||
return convert_to_bool(builder, val)
|
||||
|
||||
|
||||
def handle_if(
|
||||
@ -317,7 +262,9 @@ def handle_if(
|
||||
else:
|
||||
else_block = None
|
||||
|
||||
cond = handle_cond(func, module, builder, stmt.test, local_sym_tab, map_sym_tab)
|
||||
cond = handle_cond(
|
||||
func, module, builder, stmt.test, local_sym_tab, map_sym_tab, structs_sym_tab
|
||||
)
|
||||
if else_block:
|
||||
builder.cbranch(cond, then_block, else_block)
|
||||
else:
|
||||
@ -350,6 +297,27 @@ def handle_if(
|
||||
builder.position_at_end(merge_block)
|
||||
|
||||
|
||||
def handle_return(builder, stmt, local_sym_tab, ret_type):
|
||||
logger.info(f"Handling return statement: {ast.dump(stmt)}")
|
||||
if stmt.value is None:
|
||||
return _handle_none_return(builder)
|
||||
elif isinstance(stmt.value, ast.Name) and _is_xdp_name(stmt.value.id):
|
||||
return _handle_xdp_return(stmt, builder, ret_type)
|
||||
else:
|
||||
val = eval_expr(
|
||||
func=None,
|
||||
module=None,
|
||||
builder=builder,
|
||||
expr=stmt.value,
|
||||
local_sym_tab=local_sym_tab,
|
||||
map_sym_tab={},
|
||||
structs_sym_tab={},
|
||||
)
|
||||
logger.info(f"Evaluated return expression to {val}")
|
||||
builder.ret(val[0])
|
||||
return True
|
||||
|
||||
|
||||
def process_stmt(
|
||||
func,
|
||||
module,
|
||||
@ -383,61 +351,12 @@ def process_stmt(
|
||||
func, module, builder, stmt, map_sym_tab, local_sym_tab, structs_sym_tab
|
||||
)
|
||||
elif isinstance(stmt, ast.Return):
|
||||
if stmt.value is None:
|
||||
builder.ret(ir.Constant(ir.IntType(64), 0))
|
||||
did_return = True
|
||||
elif (
|
||||
isinstance(stmt.value, ast.Call)
|
||||
and isinstance(stmt.value.func, ast.Name)
|
||||
and len(stmt.value.args) == 1
|
||||
):
|
||||
if isinstance(stmt.value.args[0], ast.Constant) and isinstance(
|
||||
stmt.value.args[0].value, int
|
||||
):
|
||||
call_type = stmt.value.func.id
|
||||
if ctypes_to_ir(call_type) != ret_type:
|
||||
raise ValueError(
|
||||
"Return type mismatch: expected"
|
||||
f"{ctypes_to_ir(call_type)}, got {call_type}"
|
||||
)
|
||||
else:
|
||||
builder.ret(ir.Constant(ret_type, stmt.value.args[0].value))
|
||||
did_return = True
|
||||
elif isinstance(stmt.value.args[0], ast.BinOp):
|
||||
# TODO: Should be routed through eval_expr
|
||||
val = handle_binary_op(stmt.value.args[0], builder, None, local_sym_tab)
|
||||
if val is None:
|
||||
raise ValueError("Failed to evaluate return expression")
|
||||
if val[1] != ret_type:
|
||||
raise ValueError(
|
||||
"Return type mismatch: expected " f"{ret_type}, got {val[1]}"
|
||||
)
|
||||
builder.ret(val[0])
|
||||
did_return = True
|
||||
elif isinstance(stmt.value.args[0], ast.Name):
|
||||
if stmt.value.args[0].id in local_sym_tab:
|
||||
var = local_sym_tab[stmt.value.args[0].id].var
|
||||
val = builder.load(var)
|
||||
if val.type != ret_type:
|
||||
raise ValueError(
|
||||
"Return type mismatch: expected"
|
||||
f"{ret_type}, got {val.type}"
|
||||
)
|
||||
builder.ret(val)
|
||||
did_return = True
|
||||
else:
|
||||
raise ValueError("Failed to evaluate return expression")
|
||||
elif isinstance(stmt.value, ast.Name):
|
||||
if stmt.value.id == "XDP_PASS":
|
||||
builder.ret(ir.Constant(ret_type, 2))
|
||||
did_return = True
|
||||
elif stmt.value.id == "XDP_DROP":
|
||||
builder.ret(ir.Constant(ret_type, 1))
|
||||
did_return = True
|
||||
else:
|
||||
raise ValueError("Failed to evaluate return expression")
|
||||
else:
|
||||
raise ValueError("Unsupported return value")
|
||||
did_return = handle_return(
|
||||
builder,
|
||||
stmt,
|
||||
local_sym_tab,
|
||||
ret_type,
|
||||
)
|
||||
return did_return
|
||||
|
||||
|
||||
45
pythonbpf/functions/return_utils.py
Normal file
45
pythonbpf/functions/return_utils.py
Normal file
@ -0,0 +1,45 @@
|
||||
import logging
|
||||
import ast
|
||||
|
||||
from llvmlite import ir
|
||||
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
XDP_ACTIONS = {
|
||||
"XDP_ABORTED": 0,
|
||||
"XDP_DROP": 1,
|
||||
"XDP_PASS": 2,
|
||||
"XDP_TX": 3,
|
||||
"XDP_REDIRECT": 4,
|
||||
}
|
||||
|
||||
|
||||
def _handle_none_return(builder) -> bool:
|
||||
"""Handle return or return None -> returns 0."""
|
||||
builder.ret(ir.Constant(ir.IntType(64), 0))
|
||||
logger.debug("Generated default return: 0")
|
||||
return True
|
||||
|
||||
|
||||
def _is_xdp_name(name: str) -> bool:
|
||||
"""Check if a name is an XDP action"""
|
||||
return name in XDP_ACTIONS
|
||||
|
||||
|
||||
def _handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool:
|
||||
"""Handle XDP returns"""
|
||||
if not isinstance(stmt.value, ast.Name):
|
||||
return False
|
||||
|
||||
action_name = stmt.value.id
|
||||
|
||||
if action_name not in XDP_ACTIONS:
|
||||
raise ValueError(
|
||||
f"Unknown XDP action: {action_name}. Available: {XDP_ACTIONS.keys()}"
|
||||
)
|
||||
return False
|
||||
|
||||
value = XDP_ACTIONS[action_name]
|
||||
builder.ret(ir.Constant(ret_type, value))
|
||||
logger.debug(f"Generated XDP action return: {action_name} = {value}")
|
||||
return True
|
||||
@ -3,7 +3,7 @@ import logging
|
||||
from collections.abc import Callable
|
||||
|
||||
from llvmlite import ir
|
||||
from pythonbpf.expr_pass import eval_expr
|
||||
from pythonbpf.expr import eval_expr
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ from logging import Logger
|
||||
from llvmlite import ir
|
||||
from enum import Enum
|
||||
from .maps_utils import MapProcessorRegistry
|
||||
from ..debuginfo import DebugInfoGenerator
|
||||
from pythonbpf.debuginfo import DebugInfoGenerator
|
||||
import logging
|
||||
|
||||
logger: Logger = logging.getLogger(__name__)
|
||||
|
||||
@ -1,24 +1,28 @@
|
||||
from llvmlite import ir
|
||||
|
||||
# TODO: THIS IS NOT SUPPOSED TO MATCH STRINGS :skull:
|
||||
mapping = {
|
||||
"c_int8": ir.IntType(8),
|
||||
"c_uint8": ir.IntType(8),
|
||||
"c_int16": ir.IntType(16),
|
||||
"c_uint16": ir.IntType(16),
|
||||
"c_int32": ir.IntType(32),
|
||||
"c_uint32": ir.IntType(32),
|
||||
"c_int64": ir.IntType(64),
|
||||
"c_uint64": ir.IntType(64),
|
||||
"c_float": ir.FloatType(),
|
||||
"c_double": ir.DoubleType(),
|
||||
"c_void_p": ir.IntType(64),
|
||||
# Not so sure about this one
|
||||
"str": ir.PointerType(ir.IntType(8)),
|
||||
}
|
||||
|
||||
|
||||
def ctypes_to_ir(ctype: str):
|
||||
mapping = {
|
||||
"c_int8": ir.IntType(8),
|
||||
"c_uint8": ir.IntType(8),
|
||||
"c_int16": ir.IntType(16),
|
||||
"c_uint16": ir.IntType(16),
|
||||
"c_int32": ir.IntType(32),
|
||||
"c_uint32": ir.IntType(32),
|
||||
"c_int64": ir.IntType(64),
|
||||
"c_uint64": ir.IntType(64),
|
||||
"c_float": ir.FloatType(),
|
||||
"c_double": ir.DoubleType(),
|
||||
"c_void_p": ir.IntType(64),
|
||||
# Not so sure about this one
|
||||
"str": ir.PointerType(ir.IntType(8)),
|
||||
}
|
||||
if ctype in mapping:
|
||||
return mapping[ctype]
|
||||
raise NotImplementedError(f"No mapping for {ctype}")
|
||||
|
||||
|
||||
def is_ctypes(ctype: str) -> bool:
|
||||
return ctype in mapping
|
||||
|
||||
34
tests/failing_tests/conditionals/helper_cond.py
Normal file
34
tests/failing_tests/conditionals/helper_cond.py
Normal file
@ -0,0 +1,34 @@
|
||||
from pythonbpf import bpf, map, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_uint64
|
||||
from pythonbpf.maps import HashMap
|
||||
|
||||
# NOTE: Decided against fixing this
|
||||
# as a workaround is assigning the result of lookup to a variable
|
||||
# and then using that variable in the if statement.
|
||||
# Might fix in future.
|
||||
|
||||
|
||||
@bpf
|
||||
@map
|
||||
def last() -> HashMap:
|
||||
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
last.update(0, 1)
|
||||
if last.lookup(0) > 0:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
34
tests/failing_tests/conditionals/struct_ptr.py
Normal file
34
tests/failing_tests/conditionals/struct_ptr.py
Normal file
@ -0,0 +1,34 @@
|
||||
from pythonbpf import bpf, struct, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_uint64
|
||||
|
||||
# NOTE: Decided against fixing this
|
||||
# as one workaround is to just check any field of the struct
|
||||
# in the if statement. Ugly but works.
|
||||
# Might fix in future.
|
||||
|
||||
|
||||
@bpf
|
||||
@struct
|
||||
class data_t:
|
||||
pid: c_uint64
|
||||
ts: c_uint64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
dat = data_t()
|
||||
if dat:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
32
tests/passing_tests/conditionals/and.py
Normal file
32
tests/passing_tests/conditionals/and.py
Normal file
@ -0,0 +1,32 @@
|
||||
from pythonbpf import bpf, map, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_uint64
|
||||
from pythonbpf.maps import HashMap
|
||||
|
||||
|
||||
@bpf
|
||||
@map
|
||||
def last() -> HashMap:
|
||||
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
last.update(0, 1)
|
||||
last.update(1, 2)
|
||||
x = last.lookup(0)
|
||||
y = last.lookup(1)
|
||||
if x and y:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
21
tests/passing_tests/conditionals/bool.py
Normal file
21
tests/passing_tests/conditionals/bool.py
Normal file
@ -0,0 +1,21 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
if True:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
21
tests/passing_tests/conditionals/const_binop.py
Normal file
21
tests/passing_tests/conditionals/const_binop.py
Normal file
@ -0,0 +1,21 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
if (0 + 1) * 0:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
21
tests/passing_tests/conditionals/const_int.py
Normal file
21
tests/passing_tests/conditionals/const_int.py
Normal file
@ -0,0 +1,21 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
if 0:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
30
tests/passing_tests/conditionals/map.py
Normal file
30
tests/passing_tests/conditionals/map.py
Normal file
@ -0,0 +1,30 @@
|
||||
from pythonbpf import bpf, map, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_uint64
|
||||
from pythonbpf.maps import HashMap
|
||||
|
||||
|
||||
@bpf
|
||||
@map
|
||||
def last() -> HashMap:
|
||||
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
# last.update(0, 1)
|
||||
tsp = last.lookup(0)
|
||||
if tsp:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
30
tests/passing_tests/conditionals/map_comp.py
Normal file
30
tests/passing_tests/conditionals/map_comp.py
Normal file
@ -0,0 +1,30 @@
|
||||
from pythonbpf import bpf, map, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_uint64
|
||||
from pythonbpf.maps import HashMap
|
||||
|
||||
|
||||
@bpf
|
||||
@map
|
||||
def last() -> HashMap:
|
||||
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
last.update(0, 1)
|
||||
tsp = last.lookup(0)
|
||||
if tsp > 0:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
30
tests/passing_tests/conditionals/not.py
Normal file
30
tests/passing_tests/conditionals/not.py
Normal file
@ -0,0 +1,30 @@
|
||||
from pythonbpf import bpf, map, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_uint64
|
||||
from pythonbpf.maps import HashMap
|
||||
|
||||
|
||||
@bpf
|
||||
@map
|
||||
def last() -> HashMap:
|
||||
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
# last.update(0, 1)
|
||||
tsp = last.lookup(0)
|
||||
if not tsp:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
32
tests/passing_tests/conditionals/or.py
Normal file
32
tests/passing_tests/conditionals/or.py
Normal file
@ -0,0 +1,32 @@
|
||||
from pythonbpf import bpf, map, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_uint64
|
||||
from pythonbpf.maps import HashMap
|
||||
|
||||
|
||||
@bpf
|
||||
@map
|
||||
def last() -> HashMap:
|
||||
return HashMap(key=c_uint64, value=c_uint64, max_entries=3)
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
last.update(0, 1)
|
||||
# last.update(1, 2)
|
||||
x = last.lookup(0)
|
||||
y = last.lookup(1)
|
||||
if x or y:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
29
tests/passing_tests/conditionals/struct_access.py
Normal file
29
tests/passing_tests/conditionals/struct_access.py
Normal file
@ -0,0 +1,29 @@
|
||||
from pythonbpf import bpf, struct, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_uint64
|
||||
|
||||
|
||||
@bpf
|
||||
@struct
|
||||
class data_t:
|
||||
pid: c_uint64
|
||||
ts: c_uint64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
dat = data_t()
|
||||
if dat.ts:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
23
tests/passing_tests/conditionals/type_mismatch.py
Normal file
23
tests/passing_tests/conditionals/type_mismatch.py
Normal file
@ -0,0 +1,23 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64, c_int32
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
x = 0
|
||||
y = c_int32(0)
|
||||
if x == y:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
22
tests/passing_tests/conditionals/var.py
Normal file
22
tests/passing_tests/conditionals/var.py
Normal file
@ -0,0 +1,22 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
x = 0
|
||||
if x:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
22
tests/passing_tests/conditionals/var_binop.py
Normal file
22
tests/passing_tests/conditionals/var_binop.py
Normal file
@ -0,0 +1,22 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
x = 0
|
||||
if x * 1:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
22
tests/passing_tests/conditionals/var_comp.py
Normal file
22
tests/passing_tests/conditionals/var_comp.py
Normal file
@ -0,0 +1,22 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
x = 2
|
||||
if x > 3:
|
||||
print("Hello, World!")
|
||||
else:
|
||||
print("Goodbye, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
18
tests/passing_tests/return/binop_const.py
Normal file
18
tests/passing_tests/return/binop_const.py
Normal file
@ -0,0 +1,18 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
print("Hello, World!")
|
||||
return 1 + 1 - 2
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
19
tests/passing_tests/return/binop_var.py
Normal file
19
tests/passing_tests/return/binop_var.py
Normal file
@ -0,0 +1,19 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
print("Hello, World!")
|
||||
a = 2
|
||||
return a - 2
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
18
tests/passing_tests/return/bool.py
Normal file
18
tests/passing_tests/return/bool.py
Normal file
@ -0,0 +1,18 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
print("Hello, World!")
|
||||
return True
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
18
tests/passing_tests/return/int.py
Normal file
18
tests/passing_tests/return/int.py
Normal file
@ -0,0 +1,18 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
print("Hello, World!")
|
||||
return 1
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
18
tests/passing_tests/return/null.py
Normal file
18
tests/passing_tests/return/null.py
Normal file
@ -0,0 +1,18 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
print("Hello, World!")
|
||||
return
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
20
tests/passing_tests/return/typecast_binops.py
Normal file
20
tests/passing_tests/return/typecast_binops.py
Normal file
@ -0,0 +1,20 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int32
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int32:
|
||||
print("Hello, World!")
|
||||
a = 1 # int64
|
||||
x = 1 # int64
|
||||
return c_int32(a - x) # typecast to int32
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
18
tests/passing_tests/return/typecast_const.py
Normal file
18
tests/passing_tests/return/typecast_const.py
Normal file
@ -0,0 +1,18 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int32
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int32:
|
||||
print("Hello, World!")
|
||||
return c_int32(1)
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
19
tests/passing_tests/return/typecast_var.py
Normal file
19
tests/passing_tests/return/typecast_var.py
Normal file
@ -0,0 +1,19 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int32
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int32:
|
||||
print("Hello, World!")
|
||||
a = 1 # int64
|
||||
return c_int32(a) # typecast to int32
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
19
tests/passing_tests/return/var.py
Normal file
19
tests/passing_tests/return/var.py
Normal file
@ -0,0 +1,19 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
print("Hello, World!")
|
||||
a = 1
|
||||
return a
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
19
tests/passing_tests/return/xdp.py
Normal file
19
tests/passing_tests/return/xdp.py
Normal file
@ -0,0 +1,19 @@
|
||||
from pythonbpf import bpf, section, bpfglobal, compile
|
||||
from ctypes import c_void_p, c_int64
|
||||
from pythonbpf.helper import XDP_PASS
|
||||
|
||||
|
||||
@bpf
|
||||
@section("tracepoint/syscalls/sys_enter_execve")
|
||||
def hello_world(ctx: c_void_p) -> c_int64:
|
||||
print("Hello, World!")
|
||||
return XDP_PASS
|
||||
|
||||
|
||||
@bpf
|
||||
@bpfglobal
|
||||
def LICENSE() -> str:
|
||||
return "GPL"
|
||||
|
||||
|
||||
compile()
|
||||
Reference in New Issue
Block a user