Skip to content

Commit 1936ded

Browse files
committed
seperate expr handling logic to a different file to prevent circular import, add format strings
1 parent cfdc141 commit 1936ded

File tree

4 files changed

+149
-58
lines changed

4 files changed

+149
-58
lines changed

examples/execve3.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ def hello_again(ctx: c_void_p) -> c_int64:
3434
y = False
3535
if x > 0:
3636
if x < 2:
37-
print("we prevailed")
37+
print(f"we prevailed {x}")
3838
else:
39-
print("we did not prevail")
39+
print(f"we did not prevail {x}")
4040
ts = ktime()
4141
last().update(key, ts)
4242

pythonbpf/bpf_helper_handler.py

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import ast
22
from llvmlite import ir
3+
from .expr_pass import eval_expr
34

45

5-
def bpf_ktime_get_ns_emitter(call, module, builder, func):
6+
def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab=None):
67
"""
78
Emit LLVM IR for bpf_ktime_get_ns helper function call.
89
"""
@@ -62,10 +63,87 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, local_sym_tab=No
6263
return result
6364

6465

65-
def bpf_printk_emitter(call, module, builder, func):
66+
def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None):
6667
if not hasattr(func, "_fmt_counter"):
6768
func._fmt_counter = 0
6869

70+
if not call.args:
71+
raise ValueError("print expects at least one argument")
72+
73+
if isinstance(call.args[0], ast.JoinedStr):
74+
fmt_parts = []
75+
exprs = []
76+
77+
for value in call.args[0].values:
78+
if isinstance(value, ast.Constant):
79+
if isinstance(value.value, str):
80+
fmt_parts.append(value.value)
81+
elif isinstance(value.value, int):
82+
fmt_parts.append("%lld")
83+
exprs.append(ir.Constant(ir.IntType(64), value.value))
84+
else:
85+
raise NotImplementedError(
86+
"Only string and integer constants are supported in f-string.")
87+
elif isinstance(value, ast.FormattedValue):
88+
# Assume int for now
89+
fmt_parts.append("%d")
90+
if isinstance(value.value, ast.Name):
91+
exprs.append(value.value)
92+
else:
93+
raise NotImplementedError(
94+
"Only simple variable names are supported in formatted values.")
95+
else:
96+
raise NotImplementedError(
97+
"Unsupported value type in f-string.")
98+
99+
fmt_str = "".join(fmt_parts) + "\n" + "\0"
100+
fmt_name = f"{func.name}____fmt{func._fmt_counter}"
101+
func._fmt_counter += 1
102+
103+
fmt_gvar = ir.GlobalVariable(
104+
module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=fmt_name)
105+
fmt_gvar.global_constant = True
106+
fmt_gvar.initializer = ir.Constant(
107+
ir.ArrayType(ir.IntType(8), len(fmt_str)),
108+
bytearray(fmt_str.encode("utf8"))
109+
)
110+
fmt_gvar.linkage = "internal"
111+
fmt_gvar.align = 1
112+
113+
fmt_ptr = builder.bitcast(fmt_gvar, ir.PointerType())
114+
115+
args = [fmt_ptr, ir.Constant(ir.IntType(32), len(fmt_str))]
116+
117+
# Only 3 args supported in bpf_printk
118+
if len(exprs) > 3:
119+
print(
120+
"Warning: bpf_printk supports up to 3 arguments, extra arguments will be ignored.")
121+
122+
for expr in exprs[:3]:
123+
val = eval_expr(func, module, builder, expr, local_sym_tab, None)
124+
if val:
125+
if isinstance(val.type, ir.PointerType):
126+
val = builder.ptrtoint(val, ir.IntType(64))
127+
elif isinstance(val.type, ir.IntType):
128+
if val.type.width < 64:
129+
val = builder.sext(val, ir.IntType(64))
130+
else:
131+
print(
132+
"Warning: Only integer and pointer types are supported in bpf_printk arguments. Others will be converted to 0.")
133+
val = ir.Constant(ir.IntType(64), 0)
134+
args.append(val)
135+
else:
136+
print(
137+
"Warning: Failed to evaluate expression for bpf_printk argument. It will be converted to 0.")
138+
args.append(ir.Constant(ir.IntType(64), 0))
139+
140+
fn_type = ir.FunctionType(ir.IntType(
141+
64), [ir.PointerType(), ir.IntType(32)], var_arg=True)
142+
fn_ptr_type = ir.PointerType(fn_type)
143+
fn_addr = ir.Constant(ir.IntType(64), 6)
144+
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
145+
return builder.call(fn_ptr, args, tail=True)
146+
69147
for arg in call.args:
70148
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
71149
fmt_str = arg.value + "\n" + "\0"
@@ -93,6 +171,7 @@ def bpf_printk_emitter(call, module, builder, func):
93171
builder.call(fn_ptr, [fmt_ptr, ir.Constant(
94172
ir.IntType(32), len(fmt_str))], tail=True)
95173

174+
96175
def bpf_map_update_elem_emitter(call, map_ptr, module, builder, local_sym_tab=None):
97176
"""
98177
Emit LLVM IR for bpf_map_update_elem helper function call.
@@ -101,11 +180,11 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, local_sym_tab=No
101180
if not call.args or len(call.args) < 2 or len(call.args) > 3:
102181
raise ValueError("Map update expects 2 or 3 arguments (key, value, flags), got "
103182
f"{len(call.args)}")
104-
183+
105184
key_arg = call.args[0]
106185
value_arg = call.args[1]
107186
flags_arg = call.args[2] if len(call.args) > 2 else None
108-
187+
109188
# Handle key
110189
if isinstance(key_arg, ast.Name):
111190
key_name = key_arg.id
@@ -124,7 +203,7 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, local_sym_tab=No
124203
else:
125204
raise NotImplementedError(
126205
"Only simple variable names and integer constants are supported as keys in map update.")
127-
206+
128207
# Handle value
129208
if isinstance(value_arg, ast.Name):
130209
value_name = value_arg.id
@@ -143,7 +222,7 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, local_sym_tab=No
143222
else:
144223
raise NotImplementedError(
145224
"Only simple variable names and integer constants are supported as values in map update.")
146-
225+
147226
# Handle flags argument (defaults to 0)
148227
if flags_arg is not None:
149228
if isinstance(flags_arg, ast.Constant) and isinstance(flags_arg.value, int):
@@ -162,7 +241,7 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, local_sym_tab=No
162241
"Only integer constants and simple variable names are supported as flags in map update.")
163242
else:
164243
flags_val = 0
165-
244+
166245
if key_ptr is None or value_ptr is None:
167246
raise ValueError("Key pointer or value pointer is None.")
168247

@@ -173,20 +252,22 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, local_sym_tab=No
173252
var_arg=False
174253
)
175254
fn_ptr_type = ir.PointerType(fn_type)
176-
255+
177256
# helper id
178257
fn_addr = ir.Constant(ir.IntType(64), 2)
179258
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
180-
259+
181260
if isinstance(flags_val, int):
182261
flags_const = ir.Constant(ir.IntType(64), flags_val)
183262
else:
184263
flags_const = flags_val
185264

186-
result = builder.call(fn_ptr, [map_void_ptr, key_ptr, value_ptr, flags_const], tail=False)
187-
265+
result = builder.call(
266+
fn_ptr, [map_void_ptr, key_ptr, value_ptr, flags_const], tail=False)
267+
188268
return result
189269

270+
190271
helper_func_list = {
191272
"lookup": bpf_map_lookup_elem_emitter,
192273
"print": bpf_printk_emitter,
@@ -200,7 +281,7 @@ def handle_helper_call(call, module, builder, func, local_sym_tab=None, map_sym_
200281
func_name = call.func.id
201282
if func_name in helper_func_list:
202283
# it is not a map method call
203-
return helper_func_list[func_name](call, module, builder, func)
284+
return helper_func_list[func_name](call, None, module, builder, func, local_sym_tab)
204285
else:
205286
raise NotImplementedError(
206287
f"Function {func_name} is not implemented as a helper function.")

pythonbpf/expr_pass.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import ast
2+
from llvmlite import ir
3+
4+
5+
def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab):
6+
print(f"Evaluating expression: {expr}")
7+
if isinstance(expr, ast.Name):
8+
if expr.id in local_sym_tab:
9+
var = local_sym_tab[expr.id]
10+
val = builder.load(var)
11+
return val
12+
else:
13+
print(f"Undefined variable {expr.id}")
14+
return None
15+
elif isinstance(expr, ast.Constant):
16+
if isinstance(expr.value, int):
17+
return ir.Constant(ir.IntType(64), expr.value)
18+
elif isinstance(expr.value, bool):
19+
return ir.Constant(ir.IntType(1), int(expr.value))
20+
else:
21+
print("Unsupported constant type")
22+
return None
23+
elif isinstance(expr, ast.Call):
24+
# delayed import to avoid circular dependency
25+
from .bpf_helper_handler import helper_func_list, handle_helper_call
26+
27+
if isinstance(expr.func, ast.Name):
28+
# check for helpers first
29+
if expr.func.id in helper_func_list:
30+
return handle_helper_call(
31+
expr, module, builder, func, local_sym_tab, map_sym_tab)
32+
elif isinstance(expr.func, ast.Attribute):
33+
if isinstance(expr.func.value, ast.Call) and isinstance(expr.func.value.func, ast.Name):
34+
method_name = expr.func.attr
35+
if method_name in helper_func_list:
36+
return handle_helper_call(
37+
expr, module, builder, func, local_sym_tab, map_sym_tab)
38+
print("Unsupported expression evaluation")
39+
return None
40+
41+
42+
def handle_expr(func, module, builder, expr, local_sym_tab, map_sym_tab):
43+
"""Handle expression statements in the function body."""
44+
print(f"Handling expression: {ast.dump(expr)}")
45+
call = expr.value
46+
if isinstance(call, ast.Call):
47+
eval_expr(func, module, builder, call, local_sym_tab, map_sym_tab)
48+
else:
49+
print("Unsupported expression type")

pythonbpf/functions_pass.py

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from llvmlite import ir
22
import ast
33

4+
45
from .bpf_helper_handler import helper_func_list, handle_helper_call
56
from .type_deducer import ctypes_to_ir
67
from .binary_ops import handle_binary_op
8+
from .expr_pass import eval_expr, handle_expr
79

810

911
def get_probe_string(func_node):
@@ -22,6 +24,7 @@ def get_probe_string(func_node):
2224
return arg.value
2325
return "helper"
2426

27+
2528
def handle_assign(func, module, builder, stmt, map_sym_tab, local_sym_tab):
2629
"""Handle assignment statements in the function body."""
2730
if len(stmt.targets) != 1:
@@ -96,54 +99,12 @@ def handle_assign(func, module, builder, stmt, map_sym_tab, local_sym_tab):
9699
else:
97100
print("Unsupported assignment call function type")
98101
elif isinstance(rval, ast.BinOp):
99-
handle_binary_op(rval, module, builder, func, local_sym_tab, map_sym_tab)
102+
handle_binary_op(rval, module, builder, func,
103+
local_sym_tab, map_sym_tab)
100104
else:
101105
print("Unsupported assignment value type")
102106

103107

104-
def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab):
105-
if isinstance(expr, ast.Name):
106-
if expr.id in local_sym_tab:
107-
var = local_sym_tab[expr.id]
108-
val = builder.load(var)
109-
return val
110-
else:
111-
print(f"Undefined variable {expr.id}")
112-
return None
113-
elif isinstance(expr, ast.Constant):
114-
if isinstance(expr.value, int):
115-
return ir.Constant(ir.IntType(64), expr.value)
116-
elif isinstance(expr.value, bool):
117-
return ir.Constant(ir.IntType(1), int(expr.value))
118-
else:
119-
print("Unsupported constant type")
120-
return None
121-
elif isinstance(expr, ast.Call):
122-
if isinstance(expr.func, ast.Name):
123-
# check for helpers first
124-
if expr.func.id in helper_func_list:
125-
return handle_helper_call(
126-
expr, module, builder, func, local_sym_tab, map_sym_tab)
127-
elif isinstance(expr.func, ast.Attribute):
128-
if isinstance(expr.func.value, ast.Call) and isinstance(expr.func.value.func, ast.Name):
129-
method_name = expr.func.attr
130-
if method_name in helper_func_list:
131-
return handle_helper_call(
132-
expr, module, builder, func, local_sym_tab, map_sym_tab)
133-
print("Unsupported expression evaluation")
134-
return None
135-
136-
137-
def handle_expr(func, module, builder, expr, local_sym_tab, map_sym_tab):
138-
"""Handle expression statements in the function body."""
139-
print(f"Handling expression: {ast.dump(expr)}")
140-
call = expr.value
141-
if isinstance(call, ast.Call):
142-
eval_expr(func, module, builder, call, local_sym_tab, map_sym_tab)
143-
else:
144-
print("Unsupported expression type")
145-
146-
147108
def handle_cond(func, module, builder, cond, local_sym_tab, map_sym_tab):
148109
if isinstance(cond, ast.Constant):
149110
if isinstance(cond.value, bool):

0 commit comments

Comments
 (0)