Skip to content

Commit 2685d0a

Browse files
add i32 support special case and find ctx repetition in multiple functions error.
1 parent 3489f45 commit 2685d0a

File tree

6 files changed

+63
-19
lines changed

6 files changed

+63
-19
lines changed

pythonbpf/allocation_pass.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def _allocate_for_call(builder, var_name, rval, local_sym_tab, structs_sym_tab):
8181
call_type = rval.func.id
8282

8383
# C type constructors
84-
if call_type in ("c_int32", "c_int64", "c_uint32", "c_uint64"):
84+
if call_type in ("c_int32", "c_int64", "c_uint32", "c_uint64", "c_void_p"):
8585
ir_type = ctypes_to_ir(call_type)
8686
var = builder.alloca(ir_type, name=var_name)
8787
var.align = ir_type.width // 8
@@ -259,7 +259,16 @@ def _allocate_for_attribute(builder, var_name, rval, local_sym_tab, structs_sym_
259259
field_size_bits = field_size_bytes * 8
260260

261261
if field_size_bits in [8, 16, 32, 64]:
262-
actual_ir_type = ir.IntType(field_size_bits)
262+
# Special case: struct_xdp_md i32 fields should allocate as i64
263+
# because load_ctx_field will zero-extend them to i64
264+
if vmlinux_struct_name == "struct_xdp_md" and field_size_bits == 32:
265+
actual_ir_type = ir.IntType(64)
266+
logger.info(
267+
f"Allocating {var_name} as i64 for i32 field from struct_xdp_md.{field_name} "
268+
"(will be zero-extended during load)"
269+
)
270+
else:
271+
actual_ir_type = ir.IntType(field_size_bits)
263272
else:
264273
logger.warning(
265274
f"Unusual field size {field_size_bits} bits for {field_name}"

pythonbpf/assign_pass.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,26 @@ def handle_variable_assignment(
152152
if val_type != var_type:
153153
if isinstance(val_type, Field):
154154
logger.info("Handling assignment to struct field")
155+
# Special handling for struct_xdp_md i32 fields that are zero-extended to i64
156+
# The load_ctx_field already extended them, so val is i64 but val_type.type shows c_uint
157+
if (hasattr(val_type, 'type') and
158+
val_type.type.__name__ == "c_uint" and
159+
isinstance(var_type, ir.IntType) and
160+
var_type.width == 64):
161+
# This is the struct_xdp_md case - value is already i64
162+
builder.store(val, var_ptr)
163+
logger.info(f"Assigned zero-extended struct_xdp_md i32 field to {var_name} (i64)")
164+
return True
155165
# TODO: handling only ctype struct fields for now. Handle other stuff too later.
156-
if var_type == ctypes_to_ir(val_type.type.__name__):
166+
elif var_type == ctypes_to_ir(val_type.type.__name__):
157167
builder.store(val, var_ptr)
158168
logger.info(f"Assigned ctype struct field to {var_name}")
159169
return True
160-
logger.error(
161-
f"Failed to assign ctype struct field to {var_name}: {val_type} != {var_type}"
162-
)
163-
return False
170+
else:
171+
logger.error(
172+
f"Failed to assign ctype struct field to {var_name}: {val_type} != {var_type}"
173+
)
174+
return False
164175
elif isinstance(val_type, ir.IntType) and isinstance(var_type, ir.IntType):
165176
# Allow implicit int widening
166177
if val_type.width < var_type.width:

pythonbpf/vmlinux_parser/vmlinux_exports_handler.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,21 @@ def handle_vmlinux_struct_field(
9494
f"Attempting to access field {field_name} of possible vmlinux struct {struct_var_name}"
9595
)
9696
python_type: type = var_info.metadata
97+
struct_name = python_type.__name__
9798
globvar_ir, field_data = self.get_field_type(
98-
python_type.__name__, field_name
99+
struct_name, field_name
99100
)
100101
builder.function.args[0].type = ir.PointerType(ir.IntType(8))
101102
field_ptr = self.load_ctx_field(
102-
builder, builder.function.args[0], globvar_ir, field_data
103+
builder, builder.function.args[0], globvar_ir, field_data, struct_name
103104
)
104105
# Return pointer to field and field type
105106
return field_ptr, field_data
106107
else:
107108
raise RuntimeError("Variable accessed not found in symbol table")
108109

109110
@staticmethod
110-
def load_ctx_field(builder, ctx_arg, offset_global, field_data):
111+
def load_ctx_field(builder, ctx_arg, offset_global, field_data, struct_name=None):
111112
"""
112113
Generate LLVM IR to load a field from BPF context using offset.
113114
@@ -116,8 +117,9 @@ def load_ctx_field(builder, ctx_arg, offset_global, field_data):
116117
ctx_arg: The context pointer argument (ptr/i8*)
117118
offset_global: Global variable containing the field offset (i64)
118119
field_data: contains data about the field
120+
struct_name: Name of the struct being accessed (optional)
119121
Returns:
120-
The loaded value (i64 register)
122+
The loaded value (i64 register or appropriately sized)
121123
"""
122124

123125
# Load the offset value
@@ -164,6 +166,7 @@ def load_ctx_field(builder, ctx_arg, offset_global, field_data):
164166

165167
# Determine the appropriate IR type based on field information
166168
int_width = 64 # Default to 64-bit
169+
needs_zext = False # Track if we need zero-extension for xdp_md
167170

168171
if field_data is not None:
169172
# Try to determine the size from field metadata
@@ -175,6 +178,12 @@ def load_ctx_field(builder, ctx_arg, offset_global, field_data):
175178
if field_size_bits in [8, 16, 32, 64]:
176179
int_width = field_size_bits
177180
logger.info(f"Determined field size: {int_width} bits")
181+
182+
# Special handling for struct_xdp_md i32 fields
183+
# Load as i32 but extend to i64 before storing
184+
if struct_name == "struct_xdp_md" and int_width == 32:
185+
needs_zext = True
186+
logger.info(f"struct_xdp_md i32 field detected, will zero-extend to i64")
178187
else:
179188
logger.warning(
180189
f"Unusual field size {field_size_bits} bits, using default 64"
@@ -203,6 +212,11 @@ def load_ctx_field(builder, ctx_arg, offset_global, field_data):
203212
# Load and return the value
204213
value = builder.load(typed_ptr)
205214

215+
# Zero-extend i32 to i64 for struct_xdp_md fields
216+
if needs_zext:
217+
value = builder.zext(value, ir.IntType(64))
218+
logger.info("Zero-extended i32 value to i64 for struct_xdp_md field")
219+
206220
return value
207221

208222
def has_field(self, struct_name, field_name):

tests/c-form/Makefile

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
BPF_CLANG := clang
2-
CFLAGS := -O0 -emit-llvm -target bpf -c
2+
CFLAGS := -emit-llvm -target bpf -c
33

44
SRC := $(wildcard *.bpf.c)
55
LL := $(SRC:.bpf.c=.bpf.ll)
6+
LL2 := $(SRC:.bpf.c=.bpf.o2.ll)
67
OBJ := $(SRC:.bpf.c=.bpf.o)
78

89
.PHONY: all clean
910

10-
all: $(LL) $(OBJ)
11+
all: $(LL) $(OBJ) $(LL2)
1112

1213
%.bpf.o: %.bpf.c
1314
$(BPF_CLANG) -O2 -g -target bpf -c $< -o $@
1415

1516
%.bpf.ll: %.bpf.c
16-
$(BPF_CLANG) $(CFLAGS) -g -S $< -o $@
17+
$(BPF_CLANG) -O0 $(CFLAGS) -g -S $< -o $@
18+
19+
%.bpf.o2.ll: %.bpf.c
20+
$(BPF_CLANG) -O2 $(CFLAGS) -g -S $< -o $@
1721

1822
clean:
19-
rm -f $(LL) $(OBJ)
23+
rm -f $(LL) $(OBJ) $(LL2)

tests/c-form/i32test.bpf.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ SEC("xdp")
55
int print_xdp_data(struct xdp_md *ctx)
66
{
77
// 'data' is a pointer to the start of packet data
8-
void *data = (void *)(long)ctx->data;
8+
long data = (long)ctx->data;
99

10-
bpf_printk("ctx->data = %p\n", data);
10+
bpf_printk("ctx->data = %lld\n", data);
1111

1212
return XDP_PASS;
1313
}

tests/failing_tests/vmlinux/i32_test.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from ctypes import c_int64, c_int32
1+
from ctypes import c_int64, c_int32, c_void_p
22
from pythonbpf import bpf, section, bpfglobal, compile_to_ir, compile
33
from vmlinux import struct_xdp_md
44
from vmlinux import XDP_PASS
@@ -8,10 +8,16 @@
88
@section("xdp")
99
def print_xdp_data(ctx: struct_xdp_md) -> c_int64:
1010
data = ctx.data # 32-bit field: packet start pointer
11-
something = c_int32(2 + data)
11+
something = c_void_p(data)
1212
print(f"ctx->data = {something}")
1313
return c_int64(XDP_PASS)
1414

15+
@bpf
16+
@section("xdp")
17+
def print_xdp_dat2a(ct2x: struct_xdp_md) -> c_int64:
18+
data = ct2x.data # 32-bit field: packet start pointer
19+
print(f"ct2x->data = {data}")
20+
return c_int64(XDP_PASS)
1521

1622
@bpf
1723
@bpfglobal

0 commit comments

Comments
 (0)