Skip to content

Commit cb1952d

Browse files
Merge #105 (selftest roadmap tests, ArrayMap, narrow ctx stores) for bpfsnake
2 parents dc628ee + 49ed2cf commit cb1952d

57 files changed

Lines changed: 1823 additions & 111 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎docs/getting-started/quickstart.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def trace_open(ctx: c_void_p) -> c_int64:
188188
For network packet processing:
189189

190190
```python
191-
from pythonbpf.helper import XDP_PASS
191+
from vmlinux import XDP_PASS
192192

193193
@section("xdp")
194194
def xdp_pass(ctx: c_void_p) -> c_int64:

‎docs/user-guide/decorators.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def trace_open_return(ctx):
108108
For network packet processing at the earliest point:
109109

110110
```python
111-
from pythonbpf.helper import XDP_PASS
111+
from vmlinux import XDP_PASS
112112
from ctypes import c_void_p, c_int64
113113

114114
@section("xdp")

‎docs/user-guide/structs.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,8 @@ class MyStruct:
290290
```python
291291
from pythonbpf import bpf, struct, map, section
292292
from pythonbpf.maps import RingBuffer
293-
from pythonbpf.helper import ktime, XDP_PASS
293+
from pythonbpf.helper import ktime
294+
from vmlinux import XDP_PASS
294295
from ctypes import c_void_p, c_int64, c_uint8, c_uint16, c_uint32, c_uint64
295296

296297
@bpf

‎examples/xdp_pass.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from pythonbpf import bpf, map, section, bpfglobal, compile, compile_to_ir
2-
from pythonbpf.helper import XDP_PASS
2+
from vmlinux import XDP_PASS
33
from pythonbpf.maps import HashMap
44
from ctypes import c_int64, c_void_p
55

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ python_files = ["test_*.py"]
6363
markers = [
6464
"verifier: requires sudo/root for kernel verifier tests (not run by default)",
6565
"vmlinux: requires vmlinux.py for current kernel",
66+
"verifier_match: substring a verifier-level xfail expects in the rejection",
6667
]
6768
log_cli = false
6869

‎pythonbpf/allocation_pass.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pythonbpf.helper import HelperHandlerRegistry
77
from pythonbpf.vmlinux_parser.dependency_node import Field
88
from .expr import VmlinuxHandlerRegistry
9-
from pythonbpf.type_deducer import ctypes_to_ir, IntTy, signedness
9+
from pythonbpf.type_deducer import ctypes_to_ir, is_ctypes, IntTy, signedness
1010
from pythonbpf.expr.type_inference import infer_int_type
1111
from pythonbpf.maps import BPFMapType
1212

@@ -110,7 +110,9 @@ def _allocate_for_call(builder, var_name, rval, local_sym_tab, compilation_conte
110110
call_type = rval.func.id
111111

112112
# C type constructors
113-
if call_type in ("c_int32", "c_int64", "c_uint32", "c_uint64", "c_void_p"):
113+
if is_ctypes(call_type) and isinstance(ctypes_to_ir(call_type), ir.IntType):
114+
# Any integer ctypes constructor, c_uint16 included, declares a
115+
# slot of that width; the value is converted into it at the store.
114116
ir_type = ctypes_to_ir(call_type)
115117
var = builder.alloca(ir_type, name=var_name)
116118
var.align = ir_type.width // 8
@@ -206,7 +208,7 @@ def _allocate_for_map_method(
206208
return
207209

208210
map_params = map_sym_tab[map_name].params
209-
if map_params["type"] != BPFMapType.HASH:
211+
if map_params["type"] not in (BPFMapType.HASH, BPFMapType.ARRAY):
210212
logger.warning(
211213
"Map method lookup used on non-hash map, using fallback allocation"
212214
)

‎pythonbpf/assign_pass.py‎

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from llvmlite import ir
66
from pythonbpf.expr import eval_expr, convert
77
from pythonbpf.helper import emit_probe_read_kernel_str_call
8-
from pythonbpf.type_deducer import ctypes_to_ir
8+
from pythonbpf.type_deducer import field_int_type
99
from pythonbpf.vmlinux_parser.dependency_node import Field
1010

1111
logger = logging.getLogger(__name__)
@@ -40,6 +40,10 @@ def handle_struct_field_assignment(
4040
return
4141

4242
val, val_type = val_result
43+
if isinstance(val_type, Field):
44+
field_ty = field_int_type(val_type)
45+
if field_ty is not None:
46+
val_type = field_ty
4347

4448
# Special case: i8* string to [N x i8] char array
4549
if _is_char_array(field_type) and _is_i8_ptr(val_type):
@@ -150,6 +154,13 @@ def handle_variable_assignment(
150154
logger.info(
151155
f"Evaluated value for {var_name}: {val} of type {val_type}, expected {var_type}"
152156
)
157+
# An integer vmlinux field is, for conversion purposes, its declared IntTy
158+
# (width and sign from the ctype), so it takes the same convert() path as
159+
# every other integer below instead of a special case.
160+
if isinstance(val_type, Field):
161+
field_ty = field_int_type(val_type)
162+
if field_ty is not None:
163+
val_type = field_ty
153164

154165
if isinstance(val_type, ir.IntType) and isinstance(var_type, ir.IntType):
155166
# The descriptor may be narrower than the constant carrying the value
@@ -194,33 +205,13 @@ def handle_variable_assignment(
194205
)
195206
return False
196207
if isinstance(val_type, Field):
197-
logger.info("Handling assignment to struct field")
198-
field_ir_type = ctypes_to_ir(val_type.type.__name__)
199-
# Sub-register-width context fields are zero-extended to i64 by
200-
# load_ctx_field, so val is already i64 even though the field type
201-
# says otherwise (c_uint for xdp_md, c_ushort for pt_regs.cs/ss).
202-
if (
203-
isinstance(field_ir_type, ir.IntType)
204-
and field_ir_type.width < 64
205-
and isinstance(var_type, ir.IntType)
206-
and var_type.width == 64
207-
):
208-
builder.store(val, var_ptr)
209-
logger.info(
210-
f"Assigned zero-extended i{field_ir_type.width} context field "
211-
f"to {var_name} (i64)"
212-
)
213-
return True
214-
# TODO: handling only ctype struct fields for now. Handle other stuff too later.
215-
elif var_type == field_ir_type:
216-
builder.store(val, var_ptr)
217-
logger.info(f"Assigned ctype struct field to {var_name}")
218-
return True
219-
else:
220-
logger.error(
221-
f"Failed to assign ctype struct field to {var_name}: {val_type} != {var_type}"
222-
)
223-
return False
208+
# Integer fields were normalised to their IntTy above; what is
209+
# left is a pointer, array or struct field, which has no path
210+
# into this slot.
211+
logger.error(
212+
f"Failed to assign ctype struct field to {var_name}: {val_type} != {var_type}"
213+
)
214+
return False
224215
elif isinstance(val_type, ir.IntType) and isinstance(var_type, ir.PointerType):
225216
# NOTE: This is assignment to a PTR_TO_MAP_VALUE_OR_NULL
226217
logger.info(

‎pythonbpf/expr/expr_pass.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ def get_typed_operand(func, compilation_context, operand, builder, local_sym_tab
254254
vmlinux_result = VmlinuxHandlerRegistry.handle_name(operand.id)
255255
if vmlinux_result is not None:
256256
return vmlinux_result # (i64 constant, its C rank)
257+
raise SyntaxError(f"Undefined variable {operand.id}")
257258
elif isinstance(operand, ast.Constant):
258259
if isinstance(operand.value, (int, bool)):
259260
v = int(operand.value)

‎pythonbpf/functions/functions_pass.py‎

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
LocalSymbol,
3131
)
3232
from .function_debug_info import generate_function_debug_info
33-
from .return_utils import handle_none_return, handle_xdp_return, is_xdp_name
33+
from .return_utils import handle_none_return
3434
from .function_metadata import get_probe_string, is_global_function, infer_return_type
3535

3636

@@ -341,20 +341,6 @@ def handle_return(
341341
logger.info(f"Handling return statement: {ast.dump(stmt)}")
342342
if stmt.value is None:
343343
return handle_none_return(builder)
344-
elif (
345-
isinstance(stmt.value, ast.Name)
346-
and is_xdp_name(stmt.value.id)
347-
and stmt.value.id not in local_sym_tab
348-
and (
349-
compilation_context is None
350-
or stmt.value.id not in compilation_context.bpf_globals
351-
)
352-
):
353-
# The XDP fast path resolves names like XDP_PASS from the helper
354-
# constant table, but only as a fallback: a local or @bpfglobal of the
355-
# same name shadows it, mirroring C (a local shadows an enum constant)
356-
# and the resolution order everywhere else in the compiler.
357-
return handle_xdp_return(stmt, builder, ret_type)
358344
else:
359345
# Fallback for now if ctx not passed, but caller should pass it
360346
if compilation_context is None:
Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,12 @@
11
import logging
2-
import ast
32

43
from llvmlite import ir
54

65
logger: logging.Logger = logging.getLogger(__name__)
76

8-
XDP_ACTIONS = {
9-
"XDP_ABORTED": 0,
10-
"XDP_DROP": 1,
11-
"XDP_PASS": 2,
12-
"XDP_TX": 3,
13-
"XDP_REDIRECT": 4,
14-
}
15-
167

178
def handle_none_return(builder) -> bool:
189
"""Handle return or return None -> returns 0."""
1910
builder.ret(ir.Constant(ir.IntType(64), 0))
2011
logger.debug("Generated default return: 0")
2112
return True
22-
23-
24-
def is_xdp_name(name: str) -> bool:
25-
"""Check if a name is an XDP action"""
26-
return name in XDP_ACTIONS
27-
28-
29-
def handle_xdp_return(stmt: ast.Return, builder, ret_type) -> bool:
30-
"""Handle XDP returns"""
31-
if not isinstance(stmt.value, ast.Name):
32-
return False
33-
34-
action_name = stmt.value.id
35-
36-
if action_name not in XDP_ACTIONS:
37-
raise ValueError(
38-
f"Unknown XDP action: {action_name}. Available: {XDP_ACTIONS.keys()}"
39-
)
40-
41-
value = XDP_ACTIONS[action_name]
42-
builder.ret(ir.Constant(ret_type, value))
43-
logger.debug(f"Generated XDP action return: {action_name} = {value}")
44-
return True

0 commit comments

Comments
 (0)