Skip to content

Commit 204ec26

Browse files
add i32 support and make it extensible
1 parent a9d82d4 commit 204ec26

File tree

8 files changed

+153
-13
lines changed

8 files changed

+153
-13
lines changed

pythonbpf/allocation_pass.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ast
22
import logging
3-
3+
import ctypes
44
from llvmlite import ir
55
from .local_symbol import LocalSymbol
66
from pythonbpf.helper import HelperHandlerRegistry
@@ -249,7 +249,46 @@ def _allocate_for_attribute(builder, var_name, rval, local_sym_tab, structs_sym_
249249
].var = base_ptr # This is repurposing of var to store the pointer of the base type
250250
local_sym_tab[struct_var].ir_type = field_ir
251251

252-
actual_ir_type = ir.IntType(64)
252+
# Determine the actual IR type based on the field's type
253+
actual_ir_type = None
254+
255+
# Check if it's a ctypes primitive
256+
if field.type.__module__ == ctypes.__name__:
257+
try:
258+
field_size_bytes = ctypes.sizeof(field.type)
259+
field_size_bits = field_size_bytes * 8
260+
261+
if field_size_bits in [8, 16, 32, 64]:
262+
actual_ir_type = ir.IntType(field_size_bits)
263+
else:
264+
logger.warning(
265+
f"Unusual field size {field_size_bits} bits for {field_name}"
266+
)
267+
actual_ir_type = ir.IntType(64)
268+
except Exception as e:
269+
logger.warning(
270+
f"Could not determine size for ctypes field {field_name}: {e}"
271+
)
272+
actual_ir_type = ir.IntType(64)
273+
274+
# Check if it's a nested vmlinux struct or complex type
275+
elif field.type.__module__ == "vmlinux":
276+
# For pointers to structs, use pointer type (64-bit)
277+
if field.ctype_complex_type is not None and issubclass(
278+
field.ctype_complex_type, ctypes._Pointer
279+
):
280+
actual_ir_type = ir.IntType(64) # Pointer is always 64-bit
281+
# For embedded structs, this is more complex - might need different handling
282+
else:
283+
logger.warning(
284+
f"Field {field_name} is a nested vmlinux struct, using i64 for now"
285+
)
286+
actual_ir_type = ir.IntType(64)
287+
else:
288+
logger.warning(
289+
f"Unknown field type module {field.type.__module__} for {field_name}"
290+
)
291+
actual_ir_type = ir.IntType(64)
253292

254293
# Allocate with the actual IR type, not the GlobalVariable
255294
var = _allocate_with_type(builder, var_name, actual_ir_type)

pythonbpf/functions/function_debug_info.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ def generate_function_debug_info(
5959
leading_argument_name, 1, pointer_to_context_debug_info
6060
)
6161
retained_nodes = [context_local_variable]
62-
print("function name", func_node.name)
6362
subprogram_debug_info = generator.create_subprogram(
6463
func_node.name, subroutine_type, retained_nodes
6564
)

pythonbpf/type_deducer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
"c_long": ir.IntType(64),
1717
"c_ulong": ir.IntType(64),
1818
"c_longlong": ir.IntType(64),
19+
"c_uint": ir.IntType(32),
20+
"c_int": ir.IntType(32),
1921
# Not so sure about this one
2022
"str": ir.PointerType(ir.IntType(8)),
2123
}

pythonbpf/vmlinux_parser/vmlinux_exports_handler.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
from typing import Any
3-
3+
import ctypes
44
from llvmlite import ir
55

66
from pythonbpf.local_symbol import LocalSymbol
@@ -98,26 +98,24 @@ def handle_vmlinux_struct_field(
9898
python_type.__name__, field_name
9999
)
100100
builder.function.args[0].type = ir.PointerType(ir.IntType(8))
101-
print(builder.function.args[0])
102101
field_ptr = self.load_ctx_field(
103-
builder, builder.function.args[0], globvar_ir
102+
builder, builder.function.args[0], globvar_ir, field_data
104103
)
105-
print(field_ptr)
106104
# Return pointer to field and field type
107105
return field_ptr, field_data
108106
else:
109107
raise RuntimeError("Variable accessed not found in symbol table")
110108

111109
@staticmethod
112-
def load_ctx_field(builder, ctx_arg, offset_global):
110+
def load_ctx_field(builder, ctx_arg, offset_global, field_data):
113111
"""
114112
Generate LLVM IR to load a field from BPF context using offset.
115113
116114
Args:
117115
builder: llvmlite IRBuilder instance
118116
ctx_arg: The context pointer argument (ptr/i8*)
119117
offset_global: Global variable containing the field offset (i64)
120-
118+
field_data: contains data about the field
121119
Returns:
122120
The loaded value (i64 register)
123121
"""
@@ -164,9 +162,43 @@ def load_ctx_field(builder, ctx_arg, offset_global):
164162
passthrough_fn, [ir.Constant(ir.IntType(32), 0), field_ptr], tail=True
165163
)
166164

167-
# Bitcast to i64* (assuming field is 64-bit, adjust if needed)
168-
i64_ptr_type = ir.PointerType(ir.IntType(64))
169-
typed_ptr = builder.bitcast(verified_ptr, i64_ptr_type)
165+
# Determine the appropriate IR type based on field information
166+
int_width = 64 # Default to 64-bit
167+
168+
if field_data is not None:
169+
# Try to determine the size from field metadata
170+
if field_data.type.__module__ == ctypes.__name__:
171+
try:
172+
field_size_bytes = ctypes.sizeof(field_data.type)
173+
field_size_bits = field_size_bytes * 8
174+
175+
if field_size_bits in [8, 16, 32, 64]:
176+
int_width = field_size_bits
177+
logger.info(f"Determined field size: {int_width} bits")
178+
else:
179+
logger.warning(
180+
f"Unusual field size {field_size_bits} bits, using default 64"
181+
)
182+
except Exception as e:
183+
logger.warning(
184+
f"Could not determine field size: {e}, using default 64"
185+
)
186+
187+
elif field_data.type.__module__ == "vmlinux":
188+
# For pointers to structs or complex vmlinux types
189+
if field_data.ctype_complex_type is not None and issubclass(
190+
field_data.ctype_complex_type, ctypes._Pointer
191+
):
192+
int_width = 64 # Pointers are always 64-bit
193+
logger.info("Field is a pointer type, using 64 bits")
194+
# TODO: Add handling for other complex types (arrays, embedded structs, etc.)
195+
else:
196+
logger.warning("Complex vmlinux field type, using default 64 bits")
197+
198+
# Bitcast to appropriate pointer type based on determined width
199+
ptr_type = ir.PointerType(ir.IntType(int_width))
200+
201+
typed_ptr = builder.bitcast(verified_ptr, ptr_type)
170202

171203
# Load and return the value
172204
value = builder.load(typed_ptr)

tests/c-form/i32test.bpf.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <linux/bpf.h>
2+
#include <bpf/bpf_helpers.h>
3+
4+
SEC("xdp")
5+
int print_xdp_data(struct xdp_md *ctx)
6+
{
7+
// 'data' is a pointer to the start of packet data
8+
void *data = (void *)(long)ctx->data;
9+
10+
bpf_printk("ctx->data = %p\n", data);
11+
12+
return XDP_PASS;
13+
}
14+
15+
char LICENSE[] SEC("license") = "GPL";
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import logging
2+
3+
from pythonbpf import bpf, section, bpfglobal, compile_to_ir
4+
from pythonbpf import compile # noqa: F401
5+
from vmlinux import TASK_COMM_LEN # noqa: F401
6+
from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401
7+
from ctypes import c_int64, c_int32, c_void_p # noqa: F401
8+
9+
10+
# from vmlinux import struct_uinput_device
11+
# from vmlinux import struct_blk_integrity_iter
12+
13+
14+
@bpf
15+
@section("tracepoint/syscalls/sys_enter_execve")
16+
def hello_world(ctx: struct_trace_event_raw_sys_enter) -> c_int64:
17+
b = ctx.args
18+
c = b[0]
19+
print(f"This is context args field {c}")
20+
return c_int64(0)
21+
22+
23+
@bpf
24+
@bpfglobal
25+
def LICENSE() -> str:
26+
return "GPL"
27+
28+
29+
compile_to_ir("args_test.py", "args_test.ll", loglevel=logging.INFO)
30+
compile()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from ctypes import c_int64, c_int32
2+
from pythonbpf import bpf, section, bpfglobal, compile_to_ir, compile
3+
from vmlinux import struct_xdp_md
4+
from vmlinux import XDP_PASS
5+
6+
7+
@bpf
8+
@section("xdp")
9+
def print_xdp_data(ctx: struct_xdp_md) -> c_int64:
10+
data = ctx.data # 32-bit field: packet start pointer
11+
something = c_int32(2 + data)
12+
print(f"ctx->data = {something}")
13+
return c_int64(XDP_PASS)
14+
15+
16+
@bpf
17+
@bpfglobal
18+
def LICENSE() -> str:
19+
return "GPL"
20+
21+
22+
compile_to_ir("i32_test.py", "i32_test.ll")
23+
compile()

tests/passing_tests/vmlinux/simple_struct_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ def LICENSE() -> str:
4444

4545

4646
compile_to_ir("simple_struct_test.py", "simple_struct_test.ll", loglevel=logging.DEBUG)
47-
# compile()
47+
compile()

0 commit comments

Comments
 (0)