Skip to content

Commit 2539405

Browse files
allow casting
1 parent fde8eab commit 2539405

File tree

5 files changed

+91
-13
lines changed

5 files changed

+91
-13
lines changed

pythonbpf/allocation_pass.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,10 @@ def _allocate_for_call(
121121
elif VmlinuxHandlerRegistry.is_vmlinux_struct(call_type):
122122
# When calling struct_name(pointer), we're doing a cast, not construction
123123
# So we allocate as a pointer (i64) not as the actual struct
124-
ir_type = ir.IntType(64) # Pointer type
125-
var = builder.alloca(ir_type, name=var_name)
124+
var = builder.alloca(ir.PointerType(), name=var_name)
126125
var.align = 8
127126
local_sym_tab[var_name] = LocalSymbol(
128-
var, ir_type, VmlinuxHandlerRegistry.get_struct_type(call_type)
127+
var, ir.PointerType(), VmlinuxHandlerRegistry.get_struct_type(call_type)
129128
)
130129
logger.info(
131130
f"Pre-allocated {var_name} for vmlinux struct pointer cast to {call_type}"
@@ -340,11 +339,11 @@ def _allocate_for_attribute(builder, var_name, rval, local_sym_tab, structs_sym_
340339
field_ir, field = field_type
341340
# TODO: For now, we only support integer type allocations.
342341
# This always assumes first argument of function to be the context struct
343-
base_ptr = builder.function.args[0]
344-
local_sym_tab[
345-
struct_var
346-
].var = base_ptr # This is repurposing of var to store the pointer of the base type
347-
local_sym_tab[struct_var].ir_type = field_ir
342+
# base_ptr = builder.function.args[0]
343+
# local_sym_tab[
344+
# struct_var
345+
# ].var = base_ptr # This is repurposing of var to store the pointer of the base type
346+
# local_sym_tab[struct_var].ir_type = field_ir
348347

349348
# Determine the actual IR type based on the field's type
350349
actual_ir_type = None

pythonbpf/assign_pass.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import ast
22
import logging
3+
from inspect import isclass
4+
35
from llvmlite import ir
46
from pythonbpf.expr import eval_expr
57
from pythonbpf.helper import emit_probe_read_kernel_str_call
@@ -150,6 +152,9 @@ def handle_variable_assignment(
150152
val, val_type = val_result
151153
logger.info(f"Evaluated value for {var_name}: {val} of type {val_type}, {var_type}")
152154
if val_type != var_type:
155+
# if isclass(val_type) and (val_type.__module__ == "vmlinux"):
156+
# logger.info("Handling typecast to vmlinux struct")
157+
# print(val_type, var_type)
153158
if isinstance(val_type, Field):
154159
logger.info("Handling assignment to struct field")
155160
# Special handling for struct_xdp_md i32 fields that are zero-extended to i64

pythonbpf/expr/expr_pass.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,64 @@ def _handle_boolean_op(
524524
logger.error(f"Unsupported boolean operator: {type(expr.op).__name__}")
525525
return None
526526

527+
# ============================================================================
528+
# VMLinux casting
529+
# ============================================================================
530+
531+
def _handle_vmlinux_cast(
532+
func,
533+
module,
534+
builder,
535+
expr,
536+
local_sym_tab,
537+
map_sym_tab,
538+
structs_sym_tab=None,
539+
):
540+
# handle expressions such as struct_request(ctx.di) where struct_request is a vmlinux
541+
# struct and ctx.di is a pointer to a struct but is actually represented as a c_uint64
542+
# which needs to be cast to a pointer. This is also a field of another vmlinux struct
543+
"""Handle vmlinux struct cast expressions like struct_request(ctx.di)."""
544+
if len(expr.args) != 1:
545+
logger.info("vmlinux struct cast takes exactly one argument")
546+
return None
547+
548+
# Get the struct name
549+
struct_name = expr.func.id
550+
551+
# Evaluate the argument (e.g., ctx.di which is a c_uint64)
552+
arg_result = eval_expr(
553+
func,
554+
module,
555+
builder,
556+
expr.args[0],
557+
local_sym_tab,
558+
map_sym_tab,
559+
structs_sym_tab,
560+
)
561+
562+
if arg_result is None:
563+
logger.info("Failed to evaluate argument to vmlinux struct cast")
564+
return None
565+
566+
arg_val, arg_type = arg_result
567+
# Get the vmlinux struct type
568+
vmlinux_struct_type = VmlinuxHandlerRegistry.get_struct_type(struct_name)
569+
if vmlinux_struct_type is None:
570+
logger.error(f"Failed to get vmlinux struct type for {struct_name}")
571+
return None
572+
# Cast the integer/value to a pointer to the struct
573+
# If arg_val is an integer type, we need to inttoptr it
574+
ptr_type = ir.PointerType()
575+
#TODO: add a integer check here later
576+
if ctypes_to_ir(arg_type.type.__name__):
577+
# Cast integer to pointer
578+
casted_ptr = builder.inttoptr(arg_val, ptr_type)
579+
else:
580+
logger.error(f"Unsupported type for vmlinux cast: {arg_type}")
581+
return None
582+
583+
return casted_ptr, vmlinux_struct_type
584+
527585

528586
# ============================================================================
529587
# Expression Dispatcher
@@ -545,6 +603,16 @@ def eval_expr(
545603
elif isinstance(expr, ast.Constant):
546604
return _handle_constant_expr(module, builder, expr)
547605
elif isinstance(expr, ast.Call):
606+
if isinstance(expr.func, ast.Name) and VmlinuxHandlerRegistry.is_vmlinux_struct(expr.func.id):
607+
return _handle_vmlinux_cast(
608+
func,
609+
module,
610+
builder,
611+
expr,
612+
local_sym_tab,
613+
map_sym_tab,
614+
structs_sym_tab,
615+
)
548616
if isinstance(expr.func, ast.Name) and expr.func.id == "deref":
549617
return _handle_deref_call(expr, local_sym_tab, builder)
550618

tests/c-form/requests.bpf.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
#include "vmlinux.h"
22
#include <bpf/bpf_helpers.h>
33
#include <bpf/bpf_tracing.h>
4+
#include <bpf/bpf_core_read.h>
45

56
char LICENSE[] SEC("license") = "GPL";
67

78
SEC("kprobe/blk_mq_start_request")
89
int example(struct pt_regs *ctx)
910
{
11+
u64 a = ctx->r15;
1012
struct request *req = (struct request *)(ctx->di);
11-
u32 data_len = req->__data_len;
12-
bpf_printk("data length %u\n", data_len);
13+
unsigned int something_ns = BPF_CORE_READ(req, timeout);
14+
unsigned int data_len = BPF_CORE_READ(req, __data_len);
15+
bpf_printk("data length %lld %ld %ld\n", data_len, something_ns, a);
1316

1417
return 0;
1518
}
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
from vmlinux import struct_request, struct_pt_regs
2-
from pythonbpf import bpf, section, bpfglobal, compile_to_ir
2+
from pythonbpf import bpf, section, bpfglobal, compile_to_ir, compile
33
import logging
44
from ctypes import c_int64
55

66

77
@bpf
88
@section("kprobe/blk_mq_start_request")
99
def example(ctx: struct_pt_regs) -> c_int64:
10+
a = ctx.r15
1011
req = struct_request(ctx.di)
11-
c = req.__data_len
12-
print(f"data length {c}")
12+
d = req.__data_len
13+
c = req.timeout
14+
print(f"data length {d} and {c} and {a}")
1315
return c_int64(0)
1416

1517

@@ -20,3 +22,4 @@ def LICENSE() -> str:
2022

2123

2224
compile_to_ir("requests.py", "requests.ll", loglevel=logging.INFO)
25+
compile()

0 commit comments

Comments
 (0)