From 2685d0a0eed535abe3ff01bc7ca926813d17d7d7 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Wed, 5 Nov 2025 17:38:38 +0530 Subject: [PATCH] add i32 support special case and find ctx repetition in multiple functions error. --- pythonbpf/allocation_pass.py | 13 +++++++++-- pythonbpf/assign_pass.py | 21 +++++++++++++----- .../vmlinux_parser/vmlinux_exports_handler.py | 22 +++++++++++++++---- tests/c-form/Makefile | 12 ++++++---- tests/c-form/i32test.bpf.c | 4 ++-- tests/failing_tests/vmlinux/i32_test.py | 10 +++++++-- 6 files changed, 63 insertions(+), 19 deletions(-) diff --git a/pythonbpf/allocation_pass.py b/pythonbpf/allocation_pass.py index 4955f3f..8c66a9d 100644 --- a/pythonbpf/allocation_pass.py +++ b/pythonbpf/allocation_pass.py @@ -81,7 +81,7 @@ def _allocate_for_call(builder, var_name, rval, local_sym_tab, structs_sym_tab): call_type = rval.func.id # C type constructors - if call_type in ("c_int32", "c_int64", "c_uint32", "c_uint64"): + if call_type in ("c_int32", "c_int64", "c_uint32", "c_uint64", "c_void_p"): ir_type = ctypes_to_ir(call_type) var = builder.alloca(ir_type, name=var_name) var.align = ir_type.width // 8 @@ -259,7 +259,16 @@ def _allocate_for_attribute(builder, var_name, rval, local_sym_tab, structs_sym_ field_size_bits = field_size_bytes * 8 if field_size_bits in [8, 16, 32, 64]: - actual_ir_type = ir.IntType(field_size_bits) + # Special case: struct_xdp_md i32 fields should allocate as i64 + # because load_ctx_field will zero-extend them to i64 + if vmlinux_struct_name == "struct_xdp_md" and field_size_bits == 32: + actual_ir_type = ir.IntType(64) + logger.info( + f"Allocating {var_name} as i64 for i32 field from struct_xdp_md.{field_name} " + "(will be zero-extended during load)" + ) + else: + actual_ir_type = ir.IntType(field_size_bits) else: logger.warning( f"Unusual field size {field_size_bits} bits for {field_name}" diff --git a/pythonbpf/assign_pass.py b/pythonbpf/assign_pass.py index a1c2798..34f45a7 100644 --- a/pythonbpf/assign_pass.py +++ b/pythonbpf/assign_pass.py @@ -152,15 +152,26 @@ def handle_variable_assignment( if val_type != var_type: if isinstance(val_type, Field): logger.info("Handling assignment to struct field") + # Special handling for struct_xdp_md i32 fields that are zero-extended to i64 + # The load_ctx_field already extended them, so val is i64 but val_type.type shows c_uint + if (hasattr(val_type, 'type') and + val_type.type.__name__ == "c_uint" and + isinstance(var_type, ir.IntType) and + var_type.width == 64): + # This is the struct_xdp_md case - value is already i64 + builder.store(val, var_ptr) + logger.info(f"Assigned zero-extended struct_xdp_md i32 field to {var_name} (i64)") + return True # TODO: handling only ctype struct fields for now. Handle other stuff too later. - if var_type == ctypes_to_ir(val_type.type.__name__): + elif 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 + else: + 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 if val_type.width < var_type.width: diff --git a/pythonbpf/vmlinux_parser/vmlinux_exports_handler.py b/pythonbpf/vmlinux_parser/vmlinux_exports_handler.py index 6ec5988..14fae81 100644 --- a/pythonbpf/vmlinux_parser/vmlinux_exports_handler.py +++ b/pythonbpf/vmlinux_parser/vmlinux_exports_handler.py @@ -94,12 +94,13 @@ class VmlinuxHandler: f"Attempting to access field {field_name} of possible vmlinux struct {struct_var_name}" ) python_type: type = var_info.metadata + struct_name = python_type.__name__ globvar_ir, field_data = self.get_field_type( - python_type.__name__, field_name + struct_name, field_name ) builder.function.args[0].type = ir.PointerType(ir.IntType(8)) field_ptr = self.load_ctx_field( - builder, builder.function.args[0], globvar_ir, field_data + builder, builder.function.args[0], globvar_ir, field_data, struct_name ) # Return pointer to field and field type return field_ptr, field_data @@ -107,7 +108,7 @@ class VmlinuxHandler: raise RuntimeError("Variable accessed not found in symbol table") @staticmethod - def load_ctx_field(builder, ctx_arg, offset_global, field_data): + def load_ctx_field(builder, ctx_arg, offset_global, field_data, struct_name=None): """ Generate LLVM IR to load a field from BPF context using offset. @@ -116,8 +117,9 @@ class VmlinuxHandler: ctx_arg: The context pointer argument (ptr/i8*) offset_global: Global variable containing the field offset (i64) field_data: contains data about the field + struct_name: Name of the struct being accessed (optional) Returns: - The loaded value (i64 register) + The loaded value (i64 register or appropriately sized) """ # Load the offset value @@ -164,6 +166,7 @@ class VmlinuxHandler: # Determine the appropriate IR type based on field information int_width = 64 # Default to 64-bit + needs_zext = False # Track if we need zero-extension for xdp_md if field_data is not None: # Try to determine the size from field metadata @@ -175,6 +178,12 @@ class VmlinuxHandler: if field_size_bits in [8, 16, 32, 64]: int_width = field_size_bits logger.info(f"Determined field size: {int_width} bits") + + # Special handling for struct_xdp_md i32 fields + # Load as i32 but extend to i64 before storing + if struct_name == "struct_xdp_md" and int_width == 32: + needs_zext = True + logger.info(f"struct_xdp_md i32 field detected, will zero-extend to i64") else: logger.warning( f"Unusual field size {field_size_bits} bits, using default 64" @@ -203,6 +212,11 @@ class VmlinuxHandler: # Load and return the value value = builder.load(typed_ptr) + # Zero-extend i32 to i64 for struct_xdp_md fields + if needs_zext: + value = builder.zext(value, ir.IntType(64)) + logger.info("Zero-extended i32 value to i64 for struct_xdp_md field") + return value def has_field(self, struct_name, field_name): diff --git a/tests/c-form/Makefile b/tests/c-form/Makefile index 64ff900..03f8ef2 100644 --- a/tests/c-form/Makefile +++ b/tests/c-form/Makefile @@ -1,19 +1,23 @@ BPF_CLANG := clang -CFLAGS := -O0 -emit-llvm -target bpf -c +CFLAGS := -emit-llvm -target bpf -c SRC := $(wildcard *.bpf.c) LL := $(SRC:.bpf.c=.bpf.ll) +LL2 := $(SRC:.bpf.c=.bpf.o2.ll) OBJ := $(SRC:.bpf.c=.bpf.o) .PHONY: all clean -all: $(LL) $(OBJ) +all: $(LL) $(OBJ) $(LL2) %.bpf.o: %.bpf.c $(BPF_CLANG) -O2 -g -target bpf -c $< -o $@ %.bpf.ll: %.bpf.c - $(BPF_CLANG) $(CFLAGS) -g -S $< -o $@ + $(BPF_CLANG) -O0 $(CFLAGS) -g -S $< -o $@ + +%.bpf.o2.ll: %.bpf.c + $(BPF_CLANG) -O2 $(CFLAGS) -g -S $< -o $@ clean: - rm -f $(LL) $(OBJ) + rm -f $(LL) $(OBJ) $(LL2) diff --git a/tests/c-form/i32test.bpf.c b/tests/c-form/i32test.bpf.c index 71b479f..8457bab 100644 --- a/tests/c-form/i32test.bpf.c +++ b/tests/c-form/i32test.bpf.c @@ -5,9 +5,9 @@ SEC("xdp") int print_xdp_data(struct xdp_md *ctx) { // 'data' is a pointer to the start of packet data - void *data = (void *)(long)ctx->data; + long data = (long)ctx->data; - bpf_printk("ctx->data = %p\n", data); + bpf_printk("ctx->data = %lld\n", data); return XDP_PASS; } diff --git a/tests/failing_tests/vmlinux/i32_test.py b/tests/failing_tests/vmlinux/i32_test.py index 86ce2b7..cff9f0a 100644 --- a/tests/failing_tests/vmlinux/i32_test.py +++ b/tests/failing_tests/vmlinux/i32_test.py @@ -1,4 +1,4 @@ -from ctypes import c_int64, c_int32 +from ctypes import c_int64, c_int32, c_void_p from pythonbpf import bpf, section, bpfglobal, compile_to_ir, compile from vmlinux import struct_xdp_md from vmlinux import XDP_PASS @@ -8,10 +8,16 @@ from vmlinux import XDP_PASS @section("xdp") def print_xdp_data(ctx: struct_xdp_md) -> c_int64: data = ctx.data # 32-bit field: packet start pointer - something = c_int32(2 + data) + something = c_void_p(data) print(f"ctx->data = {something}") return c_int64(XDP_PASS) +@bpf +@section("xdp") +def print_xdp_dat2a(ct2x: struct_xdp_md) -> c_int64: + data = ct2x.data # 32-bit field: packet start pointer + print(f"ct2x->data = {data}") + return c_int64(XDP_PASS) @bpf @bpfglobal