Skip to content

Commit 93285db

Browse files
geenrate gep IR
1 parent 1ea44dd commit 93285db

File tree

6 files changed

+90
-33
lines changed

6 files changed

+90
-33
lines changed

pythonbpf/assign_pass.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from llvmlite import ir
44
from pythonbpf.expr import eval_expr
55
from pythonbpf.helper import emit_probe_read_kernel_str_call
6+
from pythonbpf.type_deducer import ctypes_to_ir
7+
from pythonbpf.vmlinux_parser.dependency_node import Field
68

79
logger = logging.getLogger(__name__)
810

@@ -148,7 +150,16 @@ def handle_variable_assignment(
148150
val, val_type = val_result
149151
logger.info(f"Evaluated value for {var_name}: {val} of type {val_type}, {var_type}")
150152
if val_type != var_type:
151-
if isinstance(val_type, ir.IntType) and isinstance(var_type, ir.IntType):
153+
if isinstance(val_type, Field):
154+
logger.info("Handling assignment to struct field")
155+
#TODO: handling only ctype struct fields for now. Handle other stuff too later.
156+
if var_type == ctypes_to_ir(val_type.type.__name__):
157+
builder.store(val, var_ptr)
158+
logger.info(f"Assigned ctype struct field to {var_name}")
159+
return True
160+
logger.error(f"Failed to assign ctype struct field to {var_name}: {val_type} != {var_type}")
161+
return False
162+
elif isinstance(val_type, ir.IntType) and isinstance(var_type, ir.IntType):
152163
# Allow implicit int widening
153164
if val_type.width < var_type.width:
154165
val = builder.sext(val, var_type)

pythonbpf/codegen.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ def compile_to_ir(filename: str, output: str, loglevel=logging.INFO):
157157

158158
module.add_named_metadata("llvm.ident", [f"PythonBPF {VERSION}"])
159159

160-
module_string = finalize_module(str(module))
160+
module_string: str = finalize_module(str(module))
161+
module_string += '\ndeclare ptr @llvm.preserve.struct.access.index.p0.p0(ptr, i32 immarg, i32 immarg) "nocallback" "nofree" "nosync" "nounwind" "willreturn" "memory(none)"'
161162

162163
logger.info(f"IR written to {output}")
163164
with open(output, "w") as f:

pythonbpf/type_deducer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"c_float": ir.FloatType(),
1414
"c_double": ir.DoubleType(),
1515
"c_void_p": ir.IntType(64),
16+
"c_long": ir.IntType(64),
17+
"c_longlong": ir.IntType(64),
1618
# Not so sure about this one
1719
"str": ir.PointerType(ir.IntType(8)),
1820
}

pythonbpf/vmlinux_parser/vmlinux_exports_handler.py

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,6 @@ def get_vmlinux_enum_value(self, name):
7373
return value
7474
return None
7575

76-
def handle_vmlinux_struct(self, struct_name, module, builder):
77-
"""Handle vmlinux struct initializations"""
78-
if self.is_vmlinux_struct(struct_name):
79-
# TODO: Implement core-specific struct handling
80-
# This will be more complex and depends on the BTF information
81-
logger.info(f"Handling vmlinux struct {struct_name}")
82-
# Return struct type and allocated pointer
83-
# This is a stub, actual implementation will be more complex
84-
return None
85-
return None
86-
8776
def handle_vmlinux_struct_field(
8877
self, struct_var_name, field_name, module, builder, local_sym_tab
8978
):
@@ -97,26 +86,82 @@ def handle_vmlinux_struct_field(
9786
globvar_ir, field_data = self.get_field_type(
9887
python_type.__name__, field_name
9988
)
89+
builder.function.args[0].type = ir.PointerType(ir.IntType(8))
90+
print(builder.function.args[0])
91+
field_ptr = self.load_ctx_field(builder, builder.function.args[0], globvar_ir)
92+
print(field_ptr)
93+
# Return pointer to field and field type
94+
return field_ptr, field_data
95+
else:
96+
raise RuntimeError("Variable accessed not found in symbol table")
10097

101-
# Load the offset value
102-
offset = builder.load(globvar_ir)
98+
@staticmethod
99+
def load_ctx_field(builder, ctx_arg, offset_global):
100+
"""
101+
Generate LLVM IR to load a field from BPF context using offset.
103102
104-
# Convert pointer to integer
105-
i64_type = ir.IntType(64)
106-
ptr_as_int = builder.ptrtoint(var_info.var, i64_type)
103+
Args:
104+
builder: llvmlite IRBuilder instance
105+
ctx_arg: The context pointer argument (ptr/i8*)
106+
offset_global: Global variable containing the field offset (i64)
107107
108-
# Add the offset
109-
field_addr = builder.add(ptr_as_int, offset)
108+
Returns:
109+
The loaded value (i64 register)
110+
"""
110111

111-
# Convert back to pointer
112-
i8_ptr_type = ir.IntType(8).as_pointer()
113-
field_ptr_i8 = builder.inttoptr(field_addr, i8_ptr_type)
114-
logger.info(f"field_ptr_i8: {field_ptr_i8}")
112+
# Load the offset value
113+
offset = builder.load(offset_global)
115114

116-
# Return pointer to field and field type
117-
return field_ptr_i8, field_data
115+
# Ensure ctx_arg is treated as i8* (byte pointer)
116+
i8_type = ir.IntType(8)
117+
i8_ptr_type = ir.PointerType(i8_type)
118+
119+
# Cast ctx_arg to i8* if it isn't already
120+
if str(ctx_arg.type) != str(i8_ptr_type):
121+
ctx_i8_ptr = builder.bitcast(ctx_arg, i8_ptr_type)
118122
else:
119-
raise RuntimeError("Variable accessed not found in symbol table")
123+
ctx_i8_ptr = ctx_arg
124+
125+
# GEP with explicit type - this is the key fix
126+
field_ptr = builder.gep(
127+
ctx_i8_ptr,
128+
[offset],
129+
inbounds=False,
130+
)
131+
132+
# Get or declare the BPF passthrough intrinsic
133+
module = builder.function.module
134+
135+
try:
136+
passthrough_fn = module.globals.get('llvm.bpf.passthrough.p0.p0')
137+
if passthrough_fn is None:
138+
raise KeyError
139+
except (KeyError, AttributeError):
140+
passthrough_type = ir.FunctionType(
141+
i8_ptr_type,
142+
[ir.IntType(32), i8_ptr_type]
143+
)
144+
passthrough_fn = ir.Function(
145+
module,
146+
passthrough_type,
147+
name='llvm.bpf.passthrough.p0.p0'
148+
)
149+
150+
# Call passthrough to satisfy BPF verifier
151+
verified_ptr = builder.call(
152+
passthrough_fn,
153+
[ir.Constant(ir.IntType(32), 0), field_ptr],
154+
)
155+
156+
# Bitcast to i64* (assuming field is 64-bit, adjust if needed)
157+
i64_ptr_type = ir.PointerType(ir.IntType(64))
158+
typed_ptr = builder.bitcast(verified_ptr, i64_ptr_type)
159+
160+
# Load and return the value
161+
value = builder.load(typed_ptr)
162+
163+
return value
164+
120165

121166
def has_field(self, struct_name, field_name):
122167
"""Check if a vmlinux struct has a specific field"""

tests/c-form/struct_field_tests.bpf.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ There is no point of
1919
SEC("tp/syscalls/sys_enter_execve")
2020
int handle_setuid_entry(struct trace_event_raw_sys_enter *ctx) {
2121
// Access each argument separately with clear variable assignments
22-
long int arg0 = ctx->id;
23-
bpf_printk("args[0]: %d", arg0);
22+
long int id = ctx->id;
23+
bpf_printk("This is context field %d", id);
2424
/*
2525
* the IR to aim for is
2626
* %2 = alloca ptr, align 8

tests/failing_tests/vmlinux/struct_field_access.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414
@bpf
1515
@section("tracepoint/syscalls/sys_enter_execve")
1616
def hello_world(ctx: struct_trace_event_raw_sys_enter) -> c_int64:
17-
a = 2 + TASK_COMM_LEN + TASK_COMM_LEN
1817
b = ctx.id
19-
print(f"Hello, World{TASK_COMM_LEN} and {a}")
2018
print(f"This is context field {b}")
21-
return c_int64(TASK_COMM_LEN + 2)
19+
return c_int64(0)
2220

2321

2422
@bpf
@@ -28,4 +26,4 @@ def LICENSE() -> str:
2826

2927

3028
compile_to_ir("struct_field_access.py", "struct_field_access.ll", loglevel=logging.INFO)
31-
# compile()
29+
compile()

0 commit comments

Comments
 (0)