11from llvmlite import ir
22import ast
3-
4-
5- def emit_function (module : ir .Module , name : str ):
6- ret_type = ir .IntType (32 )
7- ptr_type = ir .PointerType ()
8- func_ty = ir .FunctionType (ret_type , [ptr_type ])
9-
10- func = ir .Function (module , func_ty , name )
11-
12- param = func .args [0 ]
13- param .add_attribute ("nocapture" )
14-
15- func .attributes .add ("nounwind" )
16- # func.attributes.add("\"frame-pointer\"=\"all\"")
17- # func.attributes.add("no-trapping-math", "true")
18- # func.attributes.add("stack-protector-buffer-size", "8")
19-
20- block = func .append_basic_block (name = "entry" )
21- builder = ir .IRBuilder (block )
22- fmt_gvar = module .get_global ("hello.____fmt" )
23-
24- if fmt_gvar is None :
25- # If you haven't created the format string global yet
26- print ("Warning: Format string global not found" )
27- else :
28- # Cast integer 6 to function pointer type
29- fn_type = ir .FunctionType (ir .IntType (
30- 64 ), [ptr_type , ir .IntType (32 )], var_arg = True )
31- fn_ptr_type = ir .PointerType (fn_type )
32- fn_addr = ir .Constant (ir .IntType (64 ), 6 )
33- fn_ptr = builder .inttoptr (fn_addr , fn_ptr_type )
34- # Call the function
35- builder .call (fn_ptr , [fmt_gvar , ir .Constant (ir .IntType (32 ), 14 )])
36-
37- builder .ret (ir .Constant (ret_type , 0 ))
38-
39- func .return_value .add_attribute ("noundef" )
40- func .linkage = "dso_local"
41- func .section = "kprobe/sys_clone"
42- print ("function emitted:" , name )
43- return func
44-
3+ from .type_deducer import ctypes_to_ir
454
465def get_probe_string (func_node ):
476 """Extract the probe string from the decorator of the function node."""
@@ -58,7 +17,7 @@ def get_probe_string(func_node):
5817 return "helper"
5918
6019
61- def process_func_body (module , builder , func_node , func ):
20+ def process_func_body (module , builder , func_node , func , ret_type ):
6221 """Process the body of a bpf function"""
6322 # TODO: A lot. We just have print -> bpf_trace_printk for now
6423 did_return = False
@@ -100,23 +59,28 @@ def process_func_body(module, builder, func_node, func):
10059 if stmt .value is None :
10160 builder .ret (ir .Constant (ir .IntType (32 ), 0 ))
10261 did_return = True
103- elif isinstance (stmt .value , ast .Call ) and isinstance (stmt .value .func , ast .Name ) and stmt .value .func .id == "c_int32" and len (stmt .value .args ) == 1 and isinstance (stmt .value .args [0 ], ast .Constant ) and isinstance (stmt .value .args [0 ].value , int ):
104- builder .ret (ir .Constant (ir .IntType (
105- 32 ), stmt .value .args [0 ].value ))
106- did_return = True
62+ elif isinstance (stmt .value , ast .Call ) and isinstance (stmt .value .func , ast .Name ) and len (stmt .value .args ) == 1 and isinstance (stmt .value .args [0 ], ast .Constant ) and isinstance (stmt .value .args [0 ].value , int ):
63+ call_type = stmt .value .func .id
64+ if ctypes_to_ir (call_type ) != ret_type :
65+ raise ValueError (f"Return type mismatch: expected { ctypes_to_ir (call_type )} , got { call_type } " )
66+ else :
67+ builder .ret (ir .Constant (ret_type , stmt .value .args [0 ].value ))
68+ did_return = True
10769 else :
10870 print ("Unsupported return value" )
10971 if not did_return :
11072 builder .ret (ir .Constant (ir .IntType (32 ), 0 ))
11173
11274
113- def process_bpf_chunk (func_node , module ):
75+ def process_bpf_chunk (func_node , module , return_type ):
11476 """Process a single BPF chunk (function) and emit corresponding LLVM IR."""
11577
11678 func_name = func_node .name
11779
118- # TODO: parse return type
119- ret_type = ir .IntType (32 )
80+ #TODO: The function actual arg retgurn type is parsed,
81+ # but the actual output is not. It's still very wrong. Try uncommenting the
82+ # code in execve2.py once
83+ ret_type = return_type
12084
12185 # TODO: parse parameters
12286 param_types = []
@@ -142,7 +106,7 @@ def process_bpf_chunk(func_node, module):
142106 block = func .append_basic_block (name = "entry" )
143107 builder = ir .IRBuilder (block )
144108
145- process_func_body (module , builder , func_node , func )
109+ process_func_body (module , builder , func_node , func , ret_type )
146110
147111 print (func )
148112 print (module )
@@ -154,39 +118,59 @@ def func_proc(tree, module, chunks):
154118 func_type = get_probe_string (func_node )
155119 print (f"Found probe_string of { func_node .name } : { func_type } " )
156120
157- process_bpf_chunk (func_node , module )
158-
159-
160- def functions_processing (tree , module ):
161- bpf_functions = []
162- helper_functions = []
163- for node in tree .body :
164- section_name = ""
165- if isinstance (node , ast .FunctionDef ):
166- if len (node .decorator_list ) == 1 :
167- bpf_functions .append (node )
168- node .end_lineno
169- else :
170- # IDK why this check is needed, but whatever
171- if 'helper_functions' not in locals ():
172- helper_functions .append (node )
173-
174- # TODO: implement helpers first
175-
176- for func in bpf_functions :
177- dec = func .decorator_list [0 ]
178- if (
179- isinstance (dec , ast .Call )
180- and isinstance (dec .func , ast .Name )
181- and dec .func .id == "section"
182- and len (dec .args ) == 1
183- and isinstance (dec .args [0 ], ast .Constant )
184- and isinstance (dec .args [0 ].value , str )
185- ):
186- section_name = dec .args [0 ].value
187- else :
188- print (f"ERROR: Invalid decorator for function { func .name } " )
189- continue
190-
191- # TODO: parse arguments and return type
192- emit_function (module , func .name + "func" )
121+ process_bpf_chunk (func_node , module , ctypes_to_ir (infer_return_type (func_node )))
122+
123+ def infer_return_type (func_node : ast .FunctionDef ):
124+ if not isinstance (func_node , (ast .FunctionDef , ast .AsyncFunctionDef )):
125+ raise TypeError ("Expected ast.FunctionDef" )
126+ if func_node .returns is not None :
127+ try :
128+ return ast .unparse (func_node .returns )
129+ except Exception :
130+ node = func_node .returns
131+ if isinstance (node , ast .Name ):
132+ return node .id
133+ if isinstance (node , ast .Attribute ):
134+ return getattr (node , "attr" , type (node ).__name__ )
135+ try :
136+ return str (node )
137+ except Exception :
138+ return type (node ).__name__
139+ found_type = None
140+ def _expr_type (e ):
141+ if e is None :
142+ return "None"
143+ if isinstance (e , ast .Constant ):
144+ return type (e .value ).__name__
145+ if isinstance (e , ast .Name ):
146+ return e .id
147+ if isinstance (e , ast .Call ):
148+ f = e .func
149+ if isinstance (f , ast .Name ):
150+ return f .id
151+ if isinstance (f , ast .Attribute ):
152+ try :
153+ return ast .unparse (f )
154+ except Exception :
155+ return getattr (f , "attr" , type (f ).__name__ )
156+ try :
157+ return ast .unparse (f )
158+ except Exception :
159+ return type (f ).__name__
160+ if isinstance (e , ast .Attribute ):
161+ try :
162+ return ast .unparse (e )
163+ except Exception :
164+ return getattr (e , "attr" , type (e ).__name__ )
165+ try :
166+ return ast .unparse (e )
167+ except Exception :
168+ return type (e ).__name__
169+ for node in ast .walk (func_node ):
170+ if isinstance (node , ast .Return ):
171+ t = _expr_type (node .value )
172+ if found_type is None :
173+ found_type = t
174+ elif found_type != t :
175+ raise ValueError (f"Conflicting return types: { found_type } vs { t } " )
176+ return found_type or "None"
0 commit comments