mirror of
https://github.com/varun-r-mallya/Python-BPF.git
synced 2025-12-31 21:06:25 +00:00
geenrate gep IR
This commit is contained in:
@ -3,6 +3,8 @@ import logging
|
|||||||
from llvmlite import ir
|
from llvmlite import ir
|
||||||
from pythonbpf.expr import eval_expr
|
from pythonbpf.expr import eval_expr
|
||||||
from pythonbpf.helper import emit_probe_read_kernel_str_call
|
from pythonbpf.helper import emit_probe_read_kernel_str_call
|
||||||
|
from pythonbpf.type_deducer import ctypes_to_ir
|
||||||
|
from pythonbpf.vmlinux_parser.dependency_node import Field
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -148,7 +150,16 @@ def handle_variable_assignment(
|
|||||||
val, val_type = val_result
|
val, val_type = val_result
|
||||||
logger.info(f"Evaluated value for {var_name}: {val} of type {val_type}, {var_type}")
|
logger.info(f"Evaluated value for {var_name}: {val} of type {val_type}, {var_type}")
|
||||||
if val_type != var_type:
|
if val_type != var_type:
|
||||||
if isinstance(val_type, ir.IntType) and isinstance(var_type, ir.IntType):
|
if isinstance(val_type, Field):
|
||||||
|
logger.info("Handling assignment to struct field")
|
||||||
|
#TODO: handling only ctype struct fields for now. Handle other stuff too later.
|
||||||
|
if var_type == ctypes_to_ir(val_type.type.__name__):
|
||||||
|
builder.store(val, var_ptr)
|
||||||
|
logger.info(f"Assigned ctype struct field to {var_name}")
|
||||||
|
return True
|
||||||
|
logger.error(f"Failed to assign ctype struct field to {var_name}: {val_type} != {var_type}")
|
||||||
|
return False
|
||||||
|
elif isinstance(val_type, ir.IntType) and isinstance(var_type, ir.IntType):
|
||||||
# Allow implicit int widening
|
# Allow implicit int widening
|
||||||
if val_type.width < var_type.width:
|
if val_type.width < var_type.width:
|
||||||
val = builder.sext(val, var_type)
|
val = builder.sext(val, var_type)
|
||||||
|
|||||||
@ -157,7 +157,8 @@ def compile_to_ir(filename: str, output: str, loglevel=logging.INFO):
|
|||||||
|
|
||||||
module.add_named_metadata("llvm.ident", [f"PythonBPF {VERSION}"])
|
module.add_named_metadata("llvm.ident", [f"PythonBPF {VERSION}"])
|
||||||
|
|
||||||
module_string = finalize_module(str(module))
|
module_string: str = finalize_module(str(module))
|
||||||
|
module_string += '\ndeclare ptr @llvm.preserve.struct.access.index.p0.p0(ptr, i32 immarg, i32 immarg) "nocallback" "nofree" "nosync" "nounwind" "willreturn" "memory(none)"'
|
||||||
|
|
||||||
logger.info(f"IR written to {output}")
|
logger.info(f"IR written to {output}")
|
||||||
with open(output, "w") as f:
|
with open(output, "w") as f:
|
||||||
|
|||||||
@ -13,6 +13,8 @@ mapping = {
|
|||||||
"c_float": ir.FloatType(),
|
"c_float": ir.FloatType(),
|
||||||
"c_double": ir.DoubleType(),
|
"c_double": ir.DoubleType(),
|
||||||
"c_void_p": ir.IntType(64),
|
"c_void_p": ir.IntType(64),
|
||||||
|
"c_long": ir.IntType(64),
|
||||||
|
"c_longlong": ir.IntType(64),
|
||||||
# Not so sure about this one
|
# Not so sure about this one
|
||||||
"str": ir.PointerType(ir.IntType(8)),
|
"str": ir.PointerType(ir.IntType(8)),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,17 +73,6 @@ class VmlinuxHandler:
|
|||||||
return value
|
return value
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def handle_vmlinux_struct(self, struct_name, module, builder):
|
|
||||||
"""Handle vmlinux struct initializations"""
|
|
||||||
if self.is_vmlinux_struct(struct_name):
|
|
||||||
# TODO: Implement core-specific struct handling
|
|
||||||
# This will be more complex and depends on the BTF information
|
|
||||||
logger.info(f"Handling vmlinux struct {struct_name}")
|
|
||||||
# Return struct type and allocated pointer
|
|
||||||
# This is a stub, actual implementation will be more complex
|
|
||||||
return None
|
|
||||||
return None
|
|
||||||
|
|
||||||
def handle_vmlinux_struct_field(
|
def handle_vmlinux_struct_field(
|
||||||
self, struct_var_name, field_name, module, builder, local_sym_tab
|
self, struct_var_name, field_name, module, builder, local_sym_tab
|
||||||
):
|
):
|
||||||
@ -97,27 +86,83 @@ class VmlinuxHandler:
|
|||||||
globvar_ir, field_data = self.get_field_type(
|
globvar_ir, field_data = self.get_field_type(
|
||||||
python_type.__name__, field_name
|
python_type.__name__, field_name
|
||||||
)
|
)
|
||||||
|
builder.function.args[0].type = ir.PointerType(ir.IntType(8))
|
||||||
# Load the offset value
|
print(builder.function.args[0])
|
||||||
offset = builder.load(globvar_ir)
|
field_ptr = self.load_ctx_field(builder, builder.function.args[0], globvar_ir)
|
||||||
|
print(field_ptr)
|
||||||
# Convert pointer to integer
|
|
||||||
i64_type = ir.IntType(64)
|
|
||||||
ptr_as_int = builder.ptrtoint(var_info.var, i64_type)
|
|
||||||
|
|
||||||
# Add the offset
|
|
||||||
field_addr = builder.add(ptr_as_int, offset)
|
|
||||||
|
|
||||||
# Convert back to pointer
|
|
||||||
i8_ptr_type = ir.IntType(8).as_pointer()
|
|
||||||
field_ptr_i8 = builder.inttoptr(field_addr, i8_ptr_type)
|
|
||||||
logger.info(f"field_ptr_i8: {field_ptr_i8}")
|
|
||||||
|
|
||||||
# Return pointer to field and field type
|
# Return pointer to field and field type
|
||||||
return field_ptr_i8, field_data
|
return field_ptr, field_data
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Variable accessed not found in symbol table")
|
raise RuntimeError("Variable accessed not found in symbol table")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_ctx_field(builder, ctx_arg, offset_global):
|
||||||
|
"""
|
||||||
|
Generate LLVM IR to load a field from BPF context using offset.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
builder: llvmlite IRBuilder instance
|
||||||
|
ctx_arg: The context pointer argument (ptr/i8*)
|
||||||
|
offset_global: Global variable containing the field offset (i64)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The loaded value (i64 register)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Load the offset value
|
||||||
|
offset = builder.load(offset_global)
|
||||||
|
|
||||||
|
# Ensure ctx_arg is treated as i8* (byte pointer)
|
||||||
|
i8_type = ir.IntType(8)
|
||||||
|
i8_ptr_type = ir.PointerType(i8_type)
|
||||||
|
|
||||||
|
# Cast ctx_arg to i8* if it isn't already
|
||||||
|
if str(ctx_arg.type) != str(i8_ptr_type):
|
||||||
|
ctx_i8_ptr = builder.bitcast(ctx_arg, i8_ptr_type)
|
||||||
|
else:
|
||||||
|
ctx_i8_ptr = ctx_arg
|
||||||
|
|
||||||
|
# GEP with explicit type - this is the key fix
|
||||||
|
field_ptr = builder.gep(
|
||||||
|
ctx_i8_ptr,
|
||||||
|
[offset],
|
||||||
|
inbounds=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get or declare the BPF passthrough intrinsic
|
||||||
|
module = builder.function.module
|
||||||
|
|
||||||
|
try:
|
||||||
|
passthrough_fn = module.globals.get('llvm.bpf.passthrough.p0.p0')
|
||||||
|
if passthrough_fn is None:
|
||||||
|
raise KeyError
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
passthrough_type = ir.FunctionType(
|
||||||
|
i8_ptr_type,
|
||||||
|
[ir.IntType(32), i8_ptr_type]
|
||||||
|
)
|
||||||
|
passthrough_fn = ir.Function(
|
||||||
|
module,
|
||||||
|
passthrough_type,
|
||||||
|
name='llvm.bpf.passthrough.p0.p0'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Call passthrough to satisfy BPF verifier
|
||||||
|
verified_ptr = builder.call(
|
||||||
|
passthrough_fn,
|
||||||
|
[ir.Constant(ir.IntType(32), 0), field_ptr],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Bitcast to i64* (assuming field is 64-bit, adjust if needed)
|
||||||
|
i64_ptr_type = ir.PointerType(ir.IntType(64))
|
||||||
|
typed_ptr = builder.bitcast(verified_ptr, i64_ptr_type)
|
||||||
|
|
||||||
|
# Load and return the value
|
||||||
|
value = builder.load(typed_ptr)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def has_field(self, struct_name, field_name):
|
def has_field(self, struct_name, field_name):
|
||||||
"""Check if a vmlinux struct has a specific field"""
|
"""Check if a vmlinux struct has a specific field"""
|
||||||
if self.is_vmlinux_struct(struct_name):
|
if self.is_vmlinux_struct(struct_name):
|
||||||
|
|||||||
@ -19,8 +19,8 @@ There is no point of
|
|||||||
SEC("tp/syscalls/sys_enter_execve")
|
SEC("tp/syscalls/sys_enter_execve")
|
||||||
int handle_setuid_entry(struct trace_event_raw_sys_enter *ctx) {
|
int handle_setuid_entry(struct trace_event_raw_sys_enter *ctx) {
|
||||||
// Access each argument separately with clear variable assignments
|
// Access each argument separately with clear variable assignments
|
||||||
long int arg0 = ctx->id;
|
long int id = ctx->id;
|
||||||
bpf_printk("args[0]: %d", arg0);
|
bpf_printk("This is context field %d", id);
|
||||||
/*
|
/*
|
||||||
* the IR to aim for is
|
* the IR to aim for is
|
||||||
* %2 = alloca ptr, align 8
|
* %2 = alloca ptr, align 8
|
||||||
|
|||||||
@ -14,11 +14,9 @@ from ctypes import c_int64, c_void_p # noqa: F401
|
|||||||
@bpf
|
@bpf
|
||||||
@section("tracepoint/syscalls/sys_enter_execve")
|
@section("tracepoint/syscalls/sys_enter_execve")
|
||||||
def hello_world(ctx: struct_trace_event_raw_sys_enter) -> c_int64:
|
def hello_world(ctx: struct_trace_event_raw_sys_enter) -> c_int64:
|
||||||
a = 2 + TASK_COMM_LEN + TASK_COMM_LEN
|
|
||||||
b = ctx.id
|
b = ctx.id
|
||||||
print(f"Hello, World{TASK_COMM_LEN} and {a}")
|
|
||||||
print(f"This is context field {b}")
|
print(f"This is context field {b}")
|
||||||
return c_int64(TASK_COMM_LEN + 2)
|
return c_int64(0)
|
||||||
|
|
||||||
|
|
||||||
@bpf
|
@bpf
|
||||||
@ -28,4 +26,4 @@ def LICENSE() -> str:
|
|||||||
|
|
||||||
|
|
||||||
compile_to_ir("struct_field_access.py", "struct_field_access.ll", loglevel=logging.INFO)
|
compile_to_ir("struct_field_access.py", "struct_field_access.ll", loglevel=logging.INFO)
|
||||||
# compile()
|
compile()
|
||||||
|
|||||||
Reference in New Issue
Block a user