|
| 1 | +"""Run a BPF function's IR on the host, to check what it computes. |
| 2 | +
|
| 3 | +Only for helper-free programs: the IR is retargeted to the host and JIT |
| 4 | +compiled, and the function is called with a NULL ctx. A helper call is an |
| 5 | +inttoptr to a BPF helper id, which on the host would jump to a bogus address. |
| 6 | +
|
| 7 | +Run as a script (`python -m tests.framework.host_jit file.ll func`), so the |
| 8 | +caller can put a timeout on a program that never terminates. |
| 9 | +""" |
| 10 | + |
| 11 | +import ctypes |
| 12 | +import re |
| 13 | +import subprocess |
| 14 | +import sys |
| 15 | +from pathlib import Path |
| 16 | + |
| 17 | +import llvmlite.binding as llvm |
| 18 | + |
| 19 | + |
| 20 | +def run_function(ll_text: str, func_name: str) -> int: |
| 21 | + ll_text = re.sub(r'^target (triple|datalayout) = ".*"$', "", ll_text, flags=re.M) |
| 22 | + llvm.initialize_native_target() |
| 23 | + llvm.initialize_native_asmprinter() |
| 24 | + mod = llvm.parse_assembly(ll_text) |
| 25 | + mod.verify() |
| 26 | + tm = llvm.Target.from_default_triple().create_target_machine() |
| 27 | + engine = llvm.create_mcjit_compiler(mod, tm) |
| 28 | + engine.finalize_object() |
| 29 | + |
| 30 | + ret = re.search(rf'define [^\n]*?\bi(\d+) @"?{re.escape(func_name)}"?\(', ll_text) |
| 31 | + ret_ty = {64: ctypes.c_int64, 32: ctypes.c_int32}[int(ret.group(1))] |
| 32 | + func = ctypes.CFUNCTYPE(ret_ty, ctypes.c_void_p)( |
| 33 | + engine.get_function_address(func_name) |
| 34 | + ) |
| 35 | + return func(None) |
| 36 | + |
| 37 | + |
| 38 | +def run_in_subprocess(ll_path, func_name: str, timeout: float) -> int: |
| 39 | + """Return value of func_name(NULL). Raises subprocess.TimeoutExpired if it |
| 40 | + does not return within timeout, CalledProcessError if it fails to run.""" |
| 41 | + out = subprocess.run( |
| 42 | + [sys.executable, "-m", "tests.framework.host_jit", str(ll_path), func_name], |
| 43 | + cwd=Path(__file__).parents[2], |
| 44 | + capture_output=True, |
| 45 | + text=True, |
| 46 | + timeout=timeout, |
| 47 | + check=True, |
| 48 | + ) |
| 49 | + return int(out.stdout.strip()) |
| 50 | + |
| 51 | + |
| 52 | +if __name__ == "__main__": |
| 53 | + with open(sys.argv[1]) as f: |
| 54 | + print(run_function(f.read(), sys.argv[2])) |
0 commit comments