Skip to content

Commit b848841

Browse files
authored
Merge pull request #69 from pythonbpf/symex
Add support for userspace+kernelspace stack trace example using blazesym
2 parents df981be + e9bb90c commit b848841

File tree

9 files changed

+324
-28
lines changed

9 files changed

+324
-28
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ classifiers = [
2626
]
2727
readme = "README.md"
2828
license = {text = "Apache-2.0"}
29-
requires-python = ">=3.8"
29+
requires-python = ">=3.10"
3030

3131
dependencies = [
3232
"llvmlite",

pythonbpf/codegen.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,11 @@ def compile(loglevel=logging.WARNING) -> bool:
218218
def BPF(loglevel=logging.WARNING) -> BpfObject:
219219
caller_frame = inspect.stack()[1]
220220
src = inspect.getsource(caller_frame.frame)
221-
with tempfile.NamedTemporaryFile(
222-
mode="w+", delete=True, suffix=".py"
223-
) as f, tempfile.NamedTemporaryFile(
224-
mode="w+", delete=True, suffix=".ll"
225-
) as inter, tempfile.NamedTemporaryFile(
226-
mode="w+", delete=False, suffix=".o"
227-
) as obj_file:
221+
with (
222+
tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".py") as f,
223+
tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".ll") as inter,
224+
tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".o") as obj_file,
225+
):
228226
f.write(src)
229227
f.flush()
230228
source = f.name

pythonbpf/helper/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
smp_processor_id,
1313
uid,
1414
skb_store_bytes,
15+
get_stack,
1516
XDP_DROP,
1617
XDP_PASS,
1718
)
@@ -83,6 +84,7 @@ def helper_call_handler(
8384
"smp_processor_id",
8485
"uid",
8586
"skb_store_bytes",
87+
"get_stack",
8688
"XDP_DROP",
8789
"XDP_PASS",
8890
]

pythonbpf/helper/bpf_helper_handler.py

Lines changed: 272 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@
1212
get_int_value_from_arg,
1313
)
1414
from .printk_formatter import simple_string_print, handle_fstring_print
15-
16-
from logging import Logger
15+
from pythonbpf.maps import BPFMapType
1716
import logging
1817

19-
logger: Logger = logging.getLogger(__name__)
18+
logger = logging.getLogger(__name__)
2019

2120

2221
class BPFHelperID(Enum):
@@ -33,7 +32,12 @@ class BPFHelperID(Enum):
3332
BPF_GET_CURRENT_UID_GID = 15
3433
BPF_GET_CURRENT_COMM = 16
3534
BPF_PERF_EVENT_OUTPUT = 25
35+
BPF_GET_STACK = 67
3636
BPF_PROBE_READ_KERNEL_STR = 115
37+
BPF_RINGBUF_OUTPUT = 130
38+
BPF_RINGBUF_RESERVE = 131
39+
BPF_RINGBUF_SUBMIT = 132
40+
BPF_RINGBUF_DISCARD = 133
3741

3842

3943
@HelperHandlerRegistry.register(
@@ -358,11 +362,6 @@ def bpf_get_current_pid_tgid_emitter(
358362
return pid, ir.IntType(64)
359363

360364

361-
@HelperHandlerRegistry.register(
362-
"output",
363-
param_types=[ir.PointerType(ir.IntType(8))],
364-
return_type=ir.IntType(64),
365-
)
366365
def bpf_perf_event_output_handler(
367366
call,
368367
map_ptr,
@@ -373,6 +372,10 @@ def bpf_perf_event_output_handler(
373372
struct_sym_tab=None,
374373
map_sym_tab=None,
375374
):
375+
"""
376+
Emit LLVM IR for bpf_perf_event_output helper function call.
377+
"""
378+
376379
if len(call.args) != 1:
377380
raise ValueError(
378381
f"Perf event output expects exactly one argument, got {len(call.args)}"
@@ -410,6 +413,98 @@ def bpf_perf_event_output_handler(
410413
return result, None
411414

412415

416+
def bpf_ringbuf_output_emitter(
417+
call,
418+
map_ptr,
419+
module,
420+
builder,
421+
func,
422+
local_sym_tab=None,
423+
struct_sym_tab=None,
424+
map_sym_tab=None,
425+
):
426+
"""
427+
Emit LLVM IR for bpf_ringbuf_output helper function call.
428+
"""
429+
430+
if len(call.args) != 1:
431+
raise ValueError(
432+
f"Ringbuf output expects exactly one argument, got {len(call.args)}"
433+
)
434+
data_arg = call.args[0]
435+
data_ptr, size_val = get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab)
436+
flags_val = ir.Constant(ir.IntType(64), 0)
437+
438+
map_void_ptr = builder.bitcast(map_ptr, ir.PointerType())
439+
data_void_ptr = builder.bitcast(data_ptr, ir.PointerType())
440+
fn_type = ir.FunctionType(
441+
ir.IntType(64),
442+
[
443+
ir.PointerType(),
444+
ir.PointerType(),
445+
ir.IntType(64),
446+
ir.IntType(64),
447+
],
448+
var_arg=False,
449+
)
450+
fn_ptr_type = ir.PointerType(fn_type)
451+
452+
# helper id
453+
fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_RINGBUF_OUTPUT.value)
454+
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
455+
456+
result = builder.call(
457+
fn_ptr, [map_void_ptr, data_void_ptr, size_val, flags_val], tail=False
458+
)
459+
return result, None
460+
461+
462+
@HelperHandlerRegistry.register(
463+
"output",
464+
param_types=[ir.PointerType(ir.IntType(8))],
465+
return_type=ir.IntType(64),
466+
)
467+
def handle_output_helper(
468+
call,
469+
map_ptr,
470+
module,
471+
builder,
472+
func,
473+
local_sym_tab=None,
474+
struct_sym_tab=None,
475+
map_sym_tab=None,
476+
):
477+
"""
478+
Route output helper to the appropriate emitter based on map type.
479+
"""
480+
match map_sym_tab[map_ptr.name].type:
481+
case BPFMapType.PERF_EVENT_ARRAY:
482+
return bpf_perf_event_output_handler(
483+
call,
484+
map_ptr,
485+
module,
486+
builder,
487+
func,
488+
local_sym_tab,
489+
struct_sym_tab,
490+
map_sym_tab,
491+
)
492+
case BPFMapType.RINGBUF:
493+
return bpf_ringbuf_output_emitter(
494+
call,
495+
map_ptr,
496+
module,
497+
builder,
498+
func,
499+
local_sym_tab,
500+
struct_sym_tab,
501+
map_sym_tab,
502+
)
503+
case _:
504+
logger.error("Unsupported map type for output helper.")
505+
raise NotImplementedError("Output helper for this map type is not implemented.")
506+
507+
413508
def emit_probe_read_kernel_str_call(builder, dst_ptr, dst_size, src_ptr):
414509
"""Emit LLVM IR call to bpf_probe_read_kernel_str"""
415510

@@ -711,7 +806,10 @@ def bpf_skb_store_bytes_emitter(
711806
flags_val = get_flags_val(call.args[3], builder, local_sym_tab)
712807
else:
713808
flags_val = 0
714-
flags = ir.Constant(ir.IntType(64), flags_val)
809+
if isinstance(flags_val, int):
810+
flags = ir.Constant(ir.IntType(64), flags_val)
811+
else:
812+
flags = flags_val
715813
fn_type = ir.FunctionType(
716814
ir.IntType(64),
717815
args_signature,
@@ -736,6 +834,170 @@ def bpf_skb_store_bytes_emitter(
736834
return result, ir.IntType(64)
737835

738836

837+
@HelperHandlerRegistry.register(
838+
"reserve",
839+
param_types=[ir.IntType(64)],
840+
return_type=ir.PointerType(ir.IntType(8)),
841+
)
842+
def bpf_ringbuf_reserve_emitter(
843+
call,
844+
map_ptr,
845+
module,
846+
builder,
847+
func,
848+
local_sym_tab=None,
849+
struct_sym_tab=None,
850+
map_sym_tab=None,
851+
):
852+
"""
853+
Emit LLVM IR for bpf_ringbuf_reserve helper function call.
854+
Expected call signature: ringbuf.reserve(size)
855+
"""
856+
857+
if len(call.args) != 1:
858+
raise ValueError(
859+
f"ringbuf.reserve expects exactly one argument (size), got {len(call.args)}"
860+
)
861+
862+
size_val = get_int_value_from_arg(
863+
call.args[0],
864+
func,
865+
module,
866+
builder,
867+
local_sym_tab,
868+
map_sym_tab,
869+
struct_sym_tab,
870+
)
871+
872+
map_void_ptr = builder.bitcast(map_ptr, ir.PointerType())
873+
fn_type = ir.FunctionType(
874+
ir.PointerType(ir.IntType(8)),
875+
[ir.PointerType(), ir.IntType(64)],
876+
var_arg=False,
877+
)
878+
fn_ptr_type = ir.PointerType(fn_type)
879+
880+
fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_RINGBUF_RESERVE.value)
881+
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
882+
883+
result = builder.call(fn_ptr, [map_void_ptr, size_val], tail=False)
884+
885+
return result, ir.PointerType(ir.IntType(8))
886+
887+
888+
@HelperHandlerRegistry.register(
889+
"submit",
890+
param_types=[ir.PointerType(ir.IntType(8)), ir.IntType(64)],
891+
return_type=ir.VoidType(),
892+
)
893+
def bpf_ringbuf_submit_emitter(
894+
call,
895+
map_ptr,
896+
module,
897+
builder,
898+
func,
899+
local_sym_tab=None,
900+
struct_sym_tab=None,
901+
map_sym_tab=None,
902+
):
903+
"""
904+
Emit LLVM IR for bpf_ringbuf_submit helper function call.
905+
Expected call signature: ringbuf.submit(data, flags=0)
906+
"""
907+
908+
if len(call.args) not in (1, 2):
909+
raise ValueError(
910+
f"ringbuf.submit expects 1 or 2 args (data, flags), got {len(call.args)}"
911+
)
912+
913+
data_arg = call.args[0]
914+
flags_arg = call.args[1] if len(call.args) == 2 else None
915+
916+
data_ptr = get_or_create_ptr_from_arg(
917+
func,
918+
module,
919+
data_arg,
920+
builder,
921+
local_sym_tab,
922+
map_sym_tab,
923+
struct_sym_tab,
924+
ir.PointerType(ir.IntType(8)),
925+
)
926+
927+
flags_const = get_flags_val(flags_arg, builder, local_sym_tab)
928+
if isinstance(flags_const, int):
929+
flags_const = ir.Constant(ir.IntType(64), flags_const)
930+
931+
map_void_ptr = builder.bitcast(map_ptr, ir.PointerType())
932+
fn_type = ir.FunctionType(
933+
ir.VoidType(),
934+
[ir.PointerType(), ir.PointerType(), ir.IntType(64)],
935+
var_arg=False,
936+
)
937+
fn_ptr_type = ir.PointerType(fn_type)
938+
939+
fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_RINGBUF_SUBMIT.value)
940+
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
941+
942+
result = builder.call(fn_ptr, [map_void_ptr, data_ptr, flags_const], tail=False)
943+
944+
return result, None
945+
946+
947+
@HelperHandlerRegistry.register(
948+
"get_stack",
949+
param_types=[ir.PointerType(ir.IntType(8)), ir.IntType(64)],
950+
return_type=ir.IntType(64),
951+
)
952+
def bpf_get_stack_emitter(
953+
call,
954+
map_ptr,
955+
module,
956+
builder,
957+
func,
958+
local_sym_tab=None,
959+
struct_sym_tab=None,
960+
map_sym_tab=None,
961+
):
962+
"""
963+
Emit LLVM IR for bpf_get_stack helper function call.
964+
"""
965+
if len(call.args) not in (1, 2):
966+
raise ValueError(
967+
f"get_stack expects atmost two arguments (buf, flags), got {len(call.args)}"
968+
)
969+
ctx_ptr = func.args[0] # First argument to the function is ctx
970+
buf_arg = call.args[0]
971+
flags_arg = call.args[1] if len(call.args) == 2 else None
972+
buf_ptr, buf_size = get_buffer_ptr_and_size(
973+
buf_arg, builder, local_sym_tab, struct_sym_tab
974+
)
975+
flags_val = get_flags_val(flags_arg, builder, local_sym_tab)
976+
if isinstance(flags_val, int):
977+
flags_val = ir.Constant(ir.IntType(64), flags_val)
978+
979+
buf_void_ptr = builder.bitcast(buf_ptr, ir.PointerType())
980+
fn_type = ir.FunctionType(
981+
ir.IntType(64),
982+
[
983+
ir.PointerType(ir.IntType(8)),
984+
ir.PointerType(),
985+
ir.IntType(64),
986+
ir.IntType(64),
987+
],
988+
var_arg=False,
989+
)
990+
fn_ptr_type = ir.PointerType(fn_type)
991+
fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_GET_STACK.value)
992+
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
993+
result = builder.call(
994+
fn_ptr,
995+
[ctx_ptr, buf_void_ptr, ir.Constant(ir.IntType(64), buf_size), flags_val],
996+
tail=False,
997+
)
998+
return result, ir.IntType(64)
999+
1000+
7391001
def handle_helper_call(
7401002
call,
7411003
module,
@@ -790,6 +1052,6 @@ def invoke_helper(method_name, map_ptr=None):
7901052
if not map_sym_tab or map_name not in map_sym_tab:
7911053
raise ValueError(f"Map '{map_name}' not found in symbol table")
7921054

793-
return invoke_helper(method_name, map_sym_tab[map_name])
1055+
return invoke_helper(method_name, map_sym_tab[map_name].sym)
7941056

7951057
return None

pythonbpf/helper/helpers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ def skb_store_bytes(offset, from_buf, size, flags=0):
5252
return ctypes.c_int64(0)
5353

5454

55+
def get_stack(buf, flags=0):
56+
"""get the current stack trace"""
57+
return ctypes.c_int64(0)
58+
59+
5560
XDP_ABORTED = ctypes.c_int64(0)
5661
XDP_DROP = ctypes.c_int64(1)
5762
XDP_PASS = ctypes.c_int64(2)

0 commit comments

Comments
 (0)