diff --git a/pythonbpf/allocation_pass.py b/pythonbpf/allocation_pass.py index b5fa37c..9a0870e 100644 --- a/pythonbpf/allocation_pass.py +++ b/pythonbpf/allocation_pass.py @@ -178,7 +178,7 @@ def _allocate_for_map_method( # Main variable (pointer to pointer) ir_type = ir.PointerType(ir.IntType(64)) var = builder.alloca(ir_type, name=var_name) - local_sym_tab[var_name] = LocalSymbol(var, ir_type) + local_sym_tab[var_name] = LocalSymbol(var, ir_type, value_type) # Temporary variable for computed values tmp_ir_type = value_ir_type var_tmp = builder.alloca(tmp_ir_type, name=f"{var_name}_tmp") diff --git a/pythonbpf/expr/expr_pass.py b/pythonbpf/expr/expr_pass.py index a9eab98..7e2238f 100644 --- a/pythonbpf/expr/expr_pass.py +++ b/pythonbpf/expr/expr_pass.py @@ -61,6 +61,7 @@ def _handle_constant_expr(module, builder, expr: ast.Constant): def _handle_attribute_expr( + func, expr: ast.Attribute, local_sym_tab: Dict, structs_sym_tab: Dict, @@ -76,6 +77,89 @@ def _handle_attribute_expr( logger.info( f"Variable type: {var_type}, Variable ptr: {var_ptr}, Variable Metadata: {var_metadata}" ) + # Check if this is a pointer to a struct (from map lookup) + if ( + isinstance(var_type, ir.PointerType) + and var_metadata + and isinstance(var_metadata, str) + ): + if var_metadata in structs_sym_tab: + logger.info( + f"Handling pointer to struct {var_metadata} from map lookup" + ) + + if func is None: + raise ValueError( + f"func parameter required for null-safe pointer access to {var_name}.{attr_name}" + ) + + # Load the pointer value (ptr) + struct_ptr = builder.load(var_ptr) + + # Create blocks for null check + null_check_block = builder.block + not_null_block = func.append_basic_block( + name=f"{var_name}_not_null" + ) + merge_block = func.append_basic_block(name=f"{var_name}_merge") + + # Check if pointer is null + null_ptr = ir.Constant(struct_ptr.type, None) + is_not_null = builder.icmp_signed("!=", struct_ptr, null_ptr) + logger.info(f"Inserted null check for pointer {var_name}") + + builder.cbranch(is_not_null, not_null_block, merge_block) + + # Not-null block: Access the field + builder.position_at_end(not_null_block) + + # Get struct metadata + metadata = structs_sym_tab[var_metadata] + struct_ptr = builder.bitcast( + struct_ptr, metadata.ir_type.as_pointer() + ) + + if attr_name not in metadata.fields: + raise ValueError( + f"Field '{attr_name}' not found in struct '{var_metadata}'" + ) + + # GEP to field + field_gep = metadata.gep(builder, struct_ptr, attr_name) + + # Load field value + field_val = builder.load(field_gep) + field_type = metadata.field_type(attr_name) + + logger.info( + f"Loaded field {attr_name} from struct pointer, type: {field_type}" + ) + + # Branch to merge + not_null_after_load = builder.block + builder.branch(merge_block) + + # Merge block: PHI node for the result + builder.position_at_end(merge_block) + phi = builder.phi(field_type, name=f"{var_name}_{attr_name}") + + # If null, return zero/default value + if isinstance(field_type, ir.IntType): + zero_value = ir.Constant(field_type, 0) + elif isinstance(field_type, ir.PointerType): + zero_value = ir.Constant(field_type, None) + elif isinstance(field_type, ir.ArrayType): + # For arrays, we can't easily create a zero constant + # This case is tricky - for now, just use undef + zero_value = ir.Constant(field_type, ir.Undefined) + else: + zero_value = ir.Constant(field_type, ir.Undefined) + + phi.add_incoming(zero_value, null_check_block) + phi.add_incoming(field_val, not_null_after_load) + + logger.info(f"Created PHI node for {var_name}.{attr_name}") + return phi, field_type if ( hasattr(var_metadata, "__module__") and var_metadata.__module__ == "vmlinux" @@ -568,7 +652,9 @@ def eval_expr( logger.warning(f"Unknown call: {ast.dump(expr)}") return None elif isinstance(expr, ast.Attribute): - return _handle_attribute_expr(expr, local_sym_tab, structs_sym_tab, builder) + return _handle_attribute_expr( + func, expr, local_sym_tab, structs_sym_tab, builder + ) elif isinstance(expr, ast.BinOp): return _handle_binary_op( func, diff --git a/pythonbpf/expr/ir_ops.py b/pythonbpf/expr/ir_ops.py index 70f7415..f6835e2 100644 --- a/pythonbpf/expr/ir_ops.py +++ b/pythonbpf/expr/ir_ops.py @@ -48,3 +48,10 @@ def deref_to_depth(func, builder, val, target_depth): cur_val = phi cur_type = pointee_type return cur_val + + +def deref_struct_ptr( + func, builder, struct_ptr, struct_metadata, field_name, structs_sym_tab +): + """Dereference a pointer to a struct type.""" + return deref_to_depth(func, builder, struct_ptr, 1) diff --git a/pythonbpf/maps/map_debug_info.py b/pythonbpf/maps/map_debug_info.py index 77a5888..431ab01 100644 --- a/pythonbpf/maps/map_debug_info.py +++ b/pythonbpf/maps/map_debug_info.py @@ -117,6 +117,7 @@ def _get_key_val_dbg_type(name, generator, structs_sym_tab): type_obj = structs_sym_tab.get(name) if type_obj: + logger.info(f"Found struct named {name}, generating debug type") return _get_struct_debug_type(type_obj, generator, structs_sym_tab) # Fallback to basic types @@ -165,6 +166,6 @@ def _get_struct_debug_type(struct_obj, generator, structs_sym_tab): ) elements_arr.append(member) struct_type = generator.create_struct_type( - elements_arr, struct_obj.size, is_distinct=True + elements_arr, struct_obj.size * 8, is_distinct=True ) return struct_type