From c3a512d5cf737346e0003ba2283c59433b266d5a Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Fri, 3 Oct 2025 22:20:04 +0530 Subject: [PATCH] add global support with broken generation function --- pythonbpf/codegen.py | 3 +- pythonbpf/globals_pass.py | 86 +++++++++++++++++++++++++++++----- tests/c-form/globals.bpf.c | 27 +++++++++++ tests/failing_tests/globals.py | 23 +++++++++ 4 files changed, 127 insertions(+), 12 deletions(-) diff --git a/pythonbpf/codegen.py b/pythonbpf/codegen.py index 5de23a5..e0cb7b6 100644 --- a/pythonbpf/codegen.py +++ b/pythonbpf/codegen.py @@ -4,7 +4,7 @@ from .license_pass import license_processing from .functions_pass import func_proc from .maps import maps_proc from .structs import structs_proc -from .globals_pass import globals_processing +from .globals_pass import globals_list_creation, globals_processing from .debuginfo import DW_LANG_C11, DwarfBehaviorEnum, DebugInfoGenerator import os import subprocess @@ -46,6 +46,7 @@ def processor(source_code, filename, module): license_processing(tree, module) globals_processing(tree, module) + globals_list_creation(tree, module) def compile_to_ir(filename: str, output: str, loglevel=logging.WARNING): diff --git a/pythonbpf/globals_pass.py b/pythonbpf/globals_pass.py index 1228809..86ba19f 100644 --- a/pythonbpf/globals_pass.py +++ b/pythonbpf/globals_pass.py @@ -1,8 +1,71 @@ from llvmlite import ir import ast +from llvmlite import ir +import ast +from logging import Logger +import logging +from .type_deducer import ctypes_to_ir -def emit_globals(module: ir.Module, names: list[str]): +logger: Logger = logging.getLogger(__name__) + + +def emit_global(module: ir.Module, node, name): + print("global", node.returns.id) + ty = ctypes_to_ir(node.returns.id) + + gvar = ir.GlobalVariable(module, ty, name=name) + gvar.initializer = ir.Constant(ty, initial_value) + gvar.align = 8 + gvar.linkage = "dso_local" + gvar.global_constant = False + return gvar + + +def globals_processing(tree, module): + """Process stuff decorated with @bpf and @bpfglobal except license and return the section name""" + global_sym_tab = [] + + for node in tree.body: + # Skip non-assignment and non-function nodes + if not (isinstance(node, (ast.FunctionDef, ast.AnnAssign, ast.Assign))): + continue + + # Get the name based on node type + if isinstance(node, ast.FunctionDef): + name = node.name + else: + continue + + # Check for duplicate names + if name in global_sym_tab: + raise SyntaxError(f"ERROR: Global name '{name}' previously defined") + else: + global_sym_tab.append(name) + + # Process decorated functions + if isinstance(node, ast.FunctionDef) and node.name != "LICENSE": + # Check decorators + decorators = [ + dec.id for dec in node.decorator_list if isinstance(dec, ast.Name) + ] + + if "bpf" in decorators and "bpfglobal" in decorators: + if ( + len(node.body) == 1 + and isinstance(node.body[0], ast.Return) + and node.body[0].value is not None + and isinstance(node.body[0].value, (ast.Constant, ast.Name)) + ): + emit_global(module, node, name) + return node.name + else: + logger.info(f"Invalid global expression for '{node.name}'") + return None + + return None + +def emit_llvm_compiler_used(module: ir.Module, names: list[str]): """ Emit the @llvm.compiler.used global given a list of function/global names. """ @@ -24,26 +87,27 @@ def emit_globals(module: ir.Module, names: list[str]): gv.section = "llvm.metadata" -def globals_processing(tree, module: ir.Module): +def globals_list_creation(tree, module: ir.Module): collected = ["LICENSE"] for node in tree.body: if isinstance(node, ast.FunctionDef): for dec in node.decorator_list: if ( - isinstance(dec, ast.Call) - and isinstance(dec.func, ast.Name) - and dec.func.id == "section" - and len(dec.args) == 1 - and isinstance(dec.args[0], ast.Constant) - and isinstance(dec.args[0].value, str) + isinstance(dec, ast.Call) + and isinstance(dec.func, ast.Name) + and dec.func.id == "section" + and len(dec.args) == 1 + and isinstance(dec.args[0], ast.Constant) + and isinstance(dec.args[0].value, str) ): collected.append(node.name) - elif isinstance(dec, ast.Name) and dec.id == "bpfglobal": - collected.append(node.name) + # NOTE: all globals other than + # elif isinstance(dec, ast.Name) and dec.id == "bpfglobal": + # collected.append(node.name) elif isinstance(dec, ast.Name) and dec.id == "map": collected.append(node.name) - emit_globals(module, collected) + emit_llvm_compiler_used(module, collected) diff --git a/tests/c-form/globals.bpf.c b/tests/c-form/globals.bpf.c index e69de29..588cac5 100644 --- a/tests/c-form/globals.bpf.c +++ b/tests/c-form/globals.bpf.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +#include +#include +#include +#include + +struct test_struct { + __u64 a; + __u64 b; +}; + +struct test_struct w = {}; +volatile __u64 prev_time = 0; + +SEC("tracepoint/syscalls/sys_enter_execve") +int trace_execve(void *ctx) +{ + bpf_printk("previous %ul now %ul", w.b, w.a); + __u64 ts = bpf_ktime_get_ns(); + bpf_printk("prev %ul now %ul", prev_time, ts); + w.a = ts; + w.b = prev_time; + prev_time = ts; + return 0; +} + +char LICENSE[] SEC("license") = "GPL"; diff --git a/tests/failing_tests/globals.py b/tests/failing_tests/globals.py index e69de29..e1b9222 100644 --- a/tests/failing_tests/globals.py +++ b/tests/failing_tests/globals.py @@ -0,0 +1,23 @@ +import logging + +from pythonbpf import compile, bpf, section, bpfglobal, compile_to_ir +from ctypes import c_void_p, c_int64 + +@bpf +@bpfglobal +def somevalue() -> c_int64: + return c_int64(0) + +@bpf +@section("sometag1") +def sometag(ctx: c_void_p) -> c_int64: + return c_int64(0) + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +compile_to_ir("globals.py", "globals.ll", loglevel=logging.INFO) +compile()