Skip to content

Commit db3fcec

Browse files
committed
Add function to extract kernel-defined struct names from BTF file and update related interfaces.
1 parent 19ba54e commit db3fcec

9 files changed

Lines changed: 125 additions & 21 deletions

src/btf_binary_parser.ml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ external btf_type_by_id : btf_handle -> int -> (int * string * int * int * int)
3838
external btf_type_get_members : btf_handle -> int -> (string * int) array = "btf_type_get_members_stub"
3939
external btf_resolve_type : btf_handle -> int -> string = "btf_resolve_type_stub"
4040
external btf_extract_function_signatures : btf_handle -> string list -> (string * string) list = "btf_extract_function_signatures_stub"
41+
external btf_extract_kernel_struct_names : btf_handle -> string list = "btf_extract_kernel_struct_names_stub"
4142
external btf_free : btf_handle -> unit = "btf_free_stub"
4243

4344
(** Parse BTF file and extract requested types using libbpf *)
@@ -180,4 +181,18 @@ let extract_kernel_function_signatures btf_path function_names =
180181
with
181182
| exn ->
182183
printf "Error extracting function signatures from BTF file %s: %s\n" btf_path (Printexc.to_string exn);
183-
[]
184+
[]
185+
186+
(** Extract all kernel-defined struct names from BTF file.
187+
@param btf_path Path to the binary BTF file
188+
@return List of kernel struct names *)
189+
let extract_all_kernel_struct_names btf_path =
190+
try
191+
match btf_new_from_file btf_path with
192+
| None -> []
193+
| Some btf_handle ->
194+
let struct_names = btf_extract_kernel_struct_names btf_handle in
195+
btf_free btf_handle;
196+
struct_names
197+
with
198+
| _ -> []

src/btf_binary_parser.mli

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,9 @@ val parse_btf_file : string -> string list -> btf_type_info list
3434
@param btf_path Path to the binary BTF file
3535
@param function_names List of kernel function names to extract signatures for
3636
@return List of (function_name, signature) pairs *)
37-
val extract_kernel_function_signatures : string -> string list -> (string * string) list
37+
val extract_kernel_function_signatures : string -> string list -> (string * string) list
38+
39+
(** Extract all kernel-defined struct names from BTF file.
40+
@param btf_path Path to the binary BTF file
41+
@return List of kernel struct names *)
42+
val extract_all_kernel_struct_names : string -> string list

src/btf_parser.ml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type program_template = {
3838

3939

4040
(** Check if a type name is a well-known kernel type *)
41-
let is_well_known_kernel_type = Kernel_types.is_well_known_ebpf_type
41+
let is_well_known_kernel_type ?btf_path = Kernel_types.is_well_known_ebpf_type ?btf_path
4242

4343
(** Create hardcoded enum definitions for constants that can't be extracted from BTF *)
4444
let create_hardcoded_tc_action_enum () = {
@@ -82,7 +82,7 @@ let get_program_template prog_type btf_path =
8282
kind = bt.Btf_binary_parser.kind;
8383
size = bt.Btf_binary_parser.size;
8484
members = bt.Btf_binary_parser.members;
85-
kernel_defined = is_well_known_kernel_type bt.Btf_binary_parser.name;
85+
kernel_defined = is_well_known_kernel_type ?btf_path bt.Btf_binary_parser.name;
8686
}) binary_types
8787
| Some path -> failwith (sprintf "BTF file not found: %s" path)
8888
| None -> failwith "BTF file path is required. Use --btf-vmlinux-path option."
@@ -147,7 +147,7 @@ let get_tracepoint_program_template category_event btf_path =
147147
kind = bt.Btf_binary_parser.kind;
148148
size = bt.Btf_binary_parser.size;
149149
members = bt.Btf_binary_parser.members;
150-
kernel_defined = is_well_known_kernel_type bt.Btf_binary_parser.name;
150+
kernel_defined = is_well_known_kernel_type ?btf_path bt.Btf_binary_parser.name;
151151
}) binary_types
152152
| Some path -> failwith (sprintf "BTF file not found: %s" path)
153153
| None -> failwith "BTF file path is required for tracepoint extraction. Use --btf-vmlinux-path option."
@@ -310,7 +310,11 @@ let generate_kernelscript_source template project_name =
310310
let member_strings = List.map (fun (name, typ) ->
311311
sprintf " %s: %s," name typ
312312
) members in
313-
sprintf "struct %s {\n%s\n}" type_info.name (String.concat "\n" member_strings)
313+
(* Mark kernel-defined structs for eBPF-only usage *)
314+
let kernel_marker = if type_info.kernel_defined then
315+
"// @kernel_only - This struct is only for eBPF compilation, not userspace\n"
316+
else "" in
317+
sprintf "%sstruct %s {\n%s\n}" kernel_marker type_info.name (String.concat "\n" member_strings)
314318
| None ->
315319
sprintf "// %s type (placeholder)" type_info.name)
316320
| "enum" ->

src/btf_parser.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ val get_kprobe_program_template : string -> string option -> program_template
4343
val get_tracepoint_program_template : string -> string option -> program_template
4444

4545
(** Check if a type name is a well-known eBPF kernel type *)
46-
val is_well_known_kernel_type : string -> bool
46+
val is_well_known_kernel_type : ?btf_path:string -> string -> bool
4747

4848
(** Extract struct_ops definitions from BTF and generate KernelScript code *)
4949
val extract_struct_ops_definitions : string option -> string list -> string list

src/btf_stubs.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,40 @@ value btf_extract_function_signatures_stub(value btf_handle, value function_name
577577
CAMLreturn(result_list);
578578
}
579579

580+
/* Extract all kernel-defined struct names from BTF */
581+
value btf_extract_kernel_struct_names_stub(value btf_handle) {
582+
CAMLparam1(btf_handle);
583+
CAMLlocal2(result, cons);
584+
585+
struct btf *btf = btf_of_value(btf_handle);
586+
if (!btf) {
587+
CAMLreturn(Val_emptylist);
588+
}
589+
590+
result = Val_emptylist;
591+
__u32 nr_types = btf__type_cnt(btf);
592+
593+
/* Iterate through all BTF types */
594+
for (__u32 i = 1; i < nr_types; i++) {
595+
const struct btf_type *type = btf__type_by_id(btf, i);
596+
if (!type) continue;
597+
598+
/* Check if it's a struct type */
599+
if (btf_kind(type) == BTF_KIND_STRUCT) {
600+
const char *type_name = btf__name_by_offset(btf, type->name_off);
601+
if (type_name && strlen(type_name) > 0) {
602+
/* Create a new cons cell */
603+
cons = caml_alloc(2, 0);
604+
Store_field(cons, 0, caml_copy_string(type_name));
605+
Store_field(cons, 1, result);
606+
result = cons;
607+
}
608+
}
609+
}
610+
611+
CAMLreturn(result);
612+
}
613+
580614
/* Free BTF handle */
581615
value btf_free_stub(value btf_handle) {
582616
CAMLparam1(btf_handle);

src/ir_generator.ml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2976,8 +2976,15 @@ let lower_multi_program ast symbol_table source_name =
29762976
failwith "Only one main() function is allowed";
29772977

29782978
(* Extract struct definitions from AST (single source of truth) *)
2979+
(* Filter out kernel-only structs that should not be in userspace code *)
29792980
let struct_definitions = List.filter_map (function
2980-
| Ast.StructDecl struct_def -> Some struct_def
2981+
| Ast.StructDecl struct_def ->
2982+
(* Check if the struct has @kernel_only marker in comments/attributes *)
2983+
let is_kernel_only = List.exists (function
2984+
| Ast.SimpleAttribute "kernel_only" -> true
2985+
| _ -> false
2986+
) struct_def.struct_attributes in
2987+
if is_kernel_only then None else Some struct_def
29812988
| _ -> None
29822989
) ast in
29832990

src/kernel_types.ml

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@
1414
* limitations under the License.
1515
*)
1616

17-
(** Shared module for eBPF kernel type definitions *)
17+
(** Dynamic kernel type detection using BTF parsing *)
1818

19-
(** Well-known eBPF context types that are provided by kernel headers
20-
These should be marked as kernel_defined = true **)
21-
let well_known_ebpf_types = [
19+
open Printf
20+
21+
(** Cache for BTF-extracted kernel types to avoid re-parsing *)
22+
let kernel_types_cache : (string, string list) Hashtbl.t = Hashtbl.create 16
23+
24+
(** Fallback well-known eBPF types for when BTF is not available *)
25+
let fallback_well_known_ebpf_types = [
2226
(* eBPF context structs - provided by linux/bpf.h *)
2327
"xdp_md";
2428
"__sk_buff";
@@ -44,6 +48,41 @@ let well_known_ebpf_types = [
4448
"bpf_sockopt";
4549
]
4650

47-
(** Check if a type name is a well-known eBPF kernel type *)
48-
let is_well_known_ebpf_type type_name =
49-
List.mem type_name well_known_ebpf_types
51+
(** Extract kernel types from BTF file with caching *)
52+
let get_kernel_types_from_btf btf_path =
53+
match Hashtbl.find_opt kernel_types_cache btf_path with
54+
| Some cached_types -> cached_types
55+
| None ->
56+
let kernel_types =
57+
try
58+
Btf_binary_parser.extract_all_kernel_struct_names btf_path
59+
with
60+
| _ ->
61+
printf "Warning: Failed to extract kernel types from BTF, using fallback list\n";
62+
fallback_well_known_ebpf_types
63+
in
64+
Hashtbl.add kernel_types_cache btf_path kernel_types;
65+
kernel_types
66+
67+
(** Check if a type name is a well-known eBPF kernel type.
68+
Uses BTF if available, otherwise falls back to hardcoded list. *)
69+
let is_well_known_ebpf_type ?btf_path type_name =
70+
match btf_path with
71+
| Some path when Sys.file_exists path ->
72+
let kernel_types = get_kernel_types_from_btf path in
73+
List.mem type_name kernel_types
74+
| _ ->
75+
(* Fallback to hardcoded list when BTF is not available *)
76+
List.mem type_name fallback_well_known_ebpf_types
77+
78+
(** Clear the kernel types cache (useful for testing or when BTF file changes) *)
79+
let clear_cache () =
80+
Hashtbl.clear kernel_types_cache
81+
82+
(** Get all known kernel types for the given BTF file (for debugging/inspection) *)
83+
let get_all_kernel_types ?btf_path () =
84+
match btf_path with
85+
| Some path when Sys.file_exists path ->
86+
get_kernel_types_from_btf path
87+
| _ ->
88+
fallback_well_known_ebpf_types

src/main.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ let compile_source input_file output_dir _verbose generate_makefile btf_vmlinux_
713713
Ast.StructDecl {
714714
struct_name = btf_type.Btf_parser.name;
715715
struct_fields = fields;
716-
struct_attributes = [];
716+
struct_attributes = if btf_type.Btf_parser.kernel_defined then [Ast.SimpleAttribute "kernel_only"] else [];
717717
struct_pos = { filename = "btf"; line = 1; column = 1 }
718718
}
719719
| "enum" ->
@@ -975,7 +975,7 @@ let compile_source input_file output_dir _verbose generate_makefile btf_vmlinux_
975975

976976
(* Generate userspace coordinator directly to output directory with tail call analysis *)
977977
Userspace_codegen.generate_userspace_code_from_ir
978-
~config_declarations ~type_aliases ~tail_call_analysis ~kfunc_dependencies ~resolved_imports ~symbol_table updated_optimized_ir ~output_dir:actual_output_dir input_file;
978+
~config_declarations ~type_aliases ~tail_call_analysis ~kfunc_dependencies ~resolved_imports ~symbol_table ?btf_path:btf_vmlinux_path updated_optimized_ir ~output_dir:actual_output_dir input_file;
979979

980980
(* Output directory already created earlier *)
981981

src/userspace_codegen.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2887,7 +2887,7 @@ let generate_load_function_with_tail_calls _base_name all_usage tail_call_analys
28872887
else ""
28882888

28892889
(** Generate complete userspace program from IR *)
2890-
let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(type_aliases = []) ?(tail_call_analysis = {Tail_call_analyzer.dependencies = []; prog_array_size = 0; index_mapping = Hashtbl.create 16; errors = []}) ?(kfunc_dependencies = {kfunc_definitions = []; private_functions = []; program_dependencies = []; module_name = ""}) ?(resolved_imports = []) ?symbol_table (userspace_prog : ir_userspace_program) (global_maps : ir_map_def list) (ir_multi_prog : ir_multi_program) source_filename =
2890+
let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(type_aliases = []) ?(tail_call_analysis = {Tail_call_analyzer.dependencies = []; prog_array_size = 0; index_mapping = Hashtbl.create 16; errors = []}) ?(kfunc_dependencies = {kfunc_definitions = []; private_functions = []; program_dependencies = []; module_name = ""}) ?(resolved_imports = []) ?symbol_table ?btf_path (userspace_prog : ir_userspace_program) (global_maps : ir_map_def list) (ir_multi_prog : ir_multi_program) source_filename =
28912891
(* Collect function usage information from all functions first to determine if we need BPF headers *)
28922892
let all_usage = List.fold_left (fun acc_usage func ->
28932893
let func_usage = collect_function_usage_from_ir_function ~global_variables:ir_multi_prog.global_variables func in
@@ -3027,7 +3027,7 @@ let generate_complete_userspace_program_from_ir ?(config_declarations = []) ?(ty
30273027

30283028
(* Filter out kernel-defined structs that are provided by kernel headers *)
30293029
let user_defined_ir_structs = List.filter (fun ir_struct ->
3030-
not (Kernel_types.is_well_known_ebpf_type ir_struct.struct_name) &&
3030+
not (Kernel_types.is_well_known_ebpf_type ?btf_path ir_struct.struct_name) &&
30313031
not (Struct_ops_registry.is_known_struct_ops ir_struct.struct_name)
30323032
) non_config_ir_structs in
30333033

@@ -3233,10 +3233,10 @@ void cleanup_bpf_maps(void) {
32333233
|} includes string_typedefs type_alias_definitions string_helpers enum_definitions structs_with_pinned skeleton_code all_fd_declarations map_operation_functions ringbuf_handlers ringbuf_dispatch_functions auto_bpf_init_code getopt_parsing_code bpf_helper_functions struct_ops_attach_functions functions
32343234

32353235
(** Generate userspace C code from IR multi-program *)
3236-
let generate_userspace_code_from_ir ?(config_declarations = []) ?(type_aliases = []) ?(tail_call_analysis = {Tail_call_analyzer.dependencies = []; prog_array_size = 0; index_mapping = Hashtbl.create 16; errors = []}) ?(kfunc_dependencies = {kfunc_definitions = []; private_functions = []; program_dependencies = []; module_name = ""}) ?(resolved_imports = []) ?symbol_table (ir_multi_prog : ir_multi_program) ?(output_dir = ".") source_filename =
3236+
let generate_userspace_code_from_ir ?(config_declarations = []) ?(type_aliases = []) ?(tail_call_analysis = {Tail_call_analyzer.dependencies = []; prog_array_size = 0; index_mapping = Hashtbl.create 16; errors = []}) ?(kfunc_dependencies = {kfunc_definitions = []; private_functions = []; program_dependencies = []; module_name = ""}) ?(resolved_imports = []) ?symbol_table ?btf_path (ir_multi_prog : ir_multi_program) ?(output_dir = ".") source_filename =
32373237
let content = match ir_multi_prog.userspace_program with
32383238
| Some userspace_prog ->
3239-
generate_complete_userspace_program_from_ir ~config_declarations ~type_aliases ~tail_call_analysis ~kfunc_dependencies ~resolved_imports ?symbol_table userspace_prog ir_multi_prog.global_maps ir_multi_prog source_filename
3239+
generate_complete_userspace_program_from_ir ~config_declarations ~type_aliases ~tail_call_analysis ~kfunc_dependencies ~resolved_imports ?symbol_table ?btf_path userspace_prog ir_multi_prog.global_maps ir_multi_prog source_filename
32403240
| None ->
32413241
sprintf {|#include <stdio.h>
32423242

0 commit comments

Comments
 (0)