11// SPDX-License-Identifier: Apache-2.0
2- use std:: env ;
3- use std:: fs ;
2+ use std:: collections :: HashSet ;
3+ use std:: io :: { BufRead , BufReader , Write } ;
44use std:: path:: { Path , PathBuf } ;
5+ use std:: process:: { Command , Stdio } ;
6+ use std:: { env, fs} ;
57
6- use anyhow:: { Context , Result } ;
8+ use anyhow:: { anyhow , Context , Result } ;
79use libbpf_cargo:: SkeletonBuilder ;
8- use procfs:: KernelVersion ;
10+
11+ const VMLINUX_PATH : & str = "include/vmlinux.h" ;
12+ const VMLINUX_FEATURES : [ & str ; 1 ] = [ "bpf_map_create" ] ;
913
1014/// Tells Cargo to rerun the build if the supplied file has changed
1115fn track_file ( header : & str ) {
1216 println ! ( "cargo:rerun-if-changed={header}" ) ;
1317}
1418
1519/// Converts a BPF source code path to a skeleton path for [libbpf_rs]
16- fn get_skel_path ( src_file : & Path , version : Option < & KernelVersion > ) -> String {
20+ fn get_skel_path ( src_file : & Path ) -> PathBuf {
1721 if let Some ( bpf) = src_file. file_stem ( ) {
1822 if let Some ( stem) = Path :: new ( bpf) . file_stem ( ) {
19- let mut skel_path = String :: new ( ) ;
20- skel_path. push_str ( & stem. to_string_lossy ( ) ) ;
21- // if a kernel version is specified, add that at the end of the name
22- if let Some ( version) = version {
23- skel_path. push_str ( & format ! (
24- "_{}_{}_{}" ,
25- version. major, version. minor, version. patch
26- ) ) ;
27- }
28- skel_path. push_str ( ".skel.rs" ) ;
29- return skel_path;
23+ let mut skel = PathBuf :: new ( ) ;
24+ skel. push ( stem) ;
25+ skel. set_extension ( "skel.rs" ) ;
26+ return skel;
3027 }
3128 }
3229 panic ! (
@@ -35,53 +32,14 @@ fn get_skel_path(src_file: &Path, version: Option<&KernelVersion>) -> String {
3532 ) ;
3633}
3734
38- /// Converts [KernelVersion] to BPF_CODE_VERSION to be compared against the
39- /// `KERNEL_VERSION` macro for conditional compilation
40- pub fn kernel_version_to_bpf_code_version ( version : & KernelVersion ) -> u32 {
41- ( ( version. major as u32 ) << 16 )
42- + ( ( version. minor as u32 ) << 8 )
43- + std:: cmp:: max ( version. patch as u32 , 255 )
44- }
45-
4635/// Uses [libbpf_cargo::SkeletonBuilder] to compile BPF source code to a skeleton
47- fn compile_bpf_obj (
48- src_file : & PathBuf ,
49- out_path : & Path ,
50- versions : Option < & [ KernelVersion ] > ,
51- ) -> Result < ( ) > {
52- let mut clang_args: Vec < String > = vec ! [
53- "-Iinclude" . to_string( ) ,
54- format!( "-I{}" , out_path. to_string_lossy( ) ) ,
55- ] ;
56- let mut skel_build = SkeletonBuilder :: new ( ) ;
57- skel_build. source ( src_file) ;
58- // if multiple versions are specified
59- if let Some ( versions) = versions {
60- // compile the default case (every version before the first specified)
61- clang_args. push ( "-DBPF_CODE_VERSION=0" . to_owned ( ) ) ;
62- skel_build
63- . clang_args ( & clang_args)
64- . build_and_generate ( out_path. join ( get_skel_path ( src_file, None ) ) ) ?;
65- clang_args. pop ( ) ;
66- // compile each version of the skeleton separately
67- for version in versions {
68- clang_args. push ( format ! (
69- "-DBPF_CODE_VERSION={}" ,
70- kernel_version_to_bpf_code_version( version)
71- ) ) ;
72- skel_build
73- . clang_args ( & clang_args)
74- . build_and_generate ( out_path. join ( get_skel_path ( src_file, Some ( version) ) ) ) ?;
75- clang_args. pop ( ) ;
76- }
77- }
78- // otherwise, assume all versions are supported with the same skeleton
79- else {
80- skel_build
81- . clang_args ( & clang_args)
82- . build_and_generate ( out_path. join ( get_skel_path ( src_file, None ) ) ) ?;
83- }
84- Ok ( ( ) )
36+ fn compile_bpf_obj ( src_file : & PathBuf , out_path : & Path ) -> Result < PathBuf > {
37+ let bpf_skel_path = out_path. join ( get_skel_path ( src_file) ) ;
38+ SkeletonBuilder :: new ( )
39+ . source ( src_file)
40+ . clang_args ( [ & format ! ( "-I{}" , out_path. to_string_lossy( ) ) , "-Iinclude" ] )
41+ . build_and_generate ( & bpf_skel_path) ?;
42+ Ok ( bpf_skel_path)
8543}
8644
8745#[ derive( Debug ) ]
@@ -148,14 +106,11 @@ fn generate_header_bindings(hdr_file: &Path, out_path: &Path) -> Result<PathBuf>
148106///
149107/// Copied from https://github.com/libbpf/libbpf-rs/blob/aacaec1b7dfaa4bf9112d2f4168d77dfceee499f/libbpf-cargo/src/build.rs#L55
150108fn extract_libbpf_headers_to_disk ( target_dir : & Path ) -> Result < Option < PathBuf > > {
151- use std:: fs:: OpenOptions ;
152- use std:: io:: Write ;
153-
154109 let dir = target_dir. join ( "bpf" ) ;
155110 fs:: create_dir_all ( & dir) ?;
156111 for ( filename, contents) in libbpf_rs:: libbpf_sys:: API_HEADERS . iter ( ) {
157112 let path = dir. as_path ( ) . join ( filename) ;
158- let mut file = OpenOptions :: new ( )
113+ let mut file = fs :: OpenOptions :: new ( )
159114 . write ( true )
160115 . create ( true )
161116 . truncate ( true )
@@ -170,12 +125,7 @@ fn extract_libbpf_headers_to_disk(target_dir: &Path) -> Result<Option<PathBuf>>
170125///
171126/// You can pass a vector of filenames to explicitly ignore if they are known to cause
172127/// problems for either Clang or bindgen (e.g. "vmlinux.h")
173- fn build (
174- out_path : & Path ,
175- base_path : & str ,
176- ignore_files : Vec < & str > ,
177- versions : Option < & [ KernelVersion ] > ,
178- ) -> Result < ( ) > {
128+ fn build ( out_path : & Path , base_path : & str , ignore_files : Vec < & str > ) -> Result < ( ) > {
179129 for path in fs:: read_dir ( base_path) ?
180130 . filter_map ( |r| r. ok ( ) )
181131 . map ( |r| r. path ( ) )
@@ -189,7 +139,7 @@ fn build(
189139 continue ;
190140 }
191141 if path_str. ends_with ( ".bpf.c" ) {
192- compile_bpf_obj ( & path, out_path, versions ) ?;
142+ compile_bpf_obj ( & path, out_path) ?;
193143 }
194144 if path_str. ends_with ( ".h" ) {
195145 generate_header_bindings ( & path, out_path) ?;
@@ -198,9 +148,82 @@ fn build(
198148 Ok ( ( ) )
199149}
200150
151+ // Creates vmlinux (truncates if exists)
152+ fn generate_vmlinux ( ) -> Result < ( ) > {
153+ let vmlinux_file = fs:: File :: create ( VMLINUX_PATH ) ?;
154+ // bpftool is installed in the update_test_dependencies.sh which
155+ // is run as part of the update_root_dependencies.sh
156+ let status = Command :: new ( "bpftool" )
157+ . args ( [
158+ "btf" ,
159+ "dump" ,
160+ "file" ,
161+ "/sys/kernel/btf/vmlinux" ,
162+ "format" ,
163+ "c" ,
164+ ] )
165+ . stdout ( Stdio :: from ( vmlinux_file) )
166+ . status ( ) ?;
167+ if !status. success ( ) {
168+ return Err ( anyhow ! (
169+ "failed to generate vmlinux using bpftool: {}" ,
170+ status
171+ ) ) ;
172+ }
173+
174+ Ok ( ( ) )
175+ }
176+
177+ fn detect_vmlinux_features ( ) -> Result < HashSet < String > > {
178+ // detect features
179+ let file = fs:: File :: open ( VMLINUX_PATH ) ?;
180+ let reader = BufReader :: new ( file) ;
181+ let mut found = HashSet :: new ( ) ;
182+ for line in reader. lines ( ) {
183+ let line = line?;
184+ for feat in VMLINUX_FEATURES {
185+ if line. contains ( feat) {
186+ found. insert ( feat. to_string ( ) ) ;
187+ }
188+ }
189+ }
190+
191+ // validate features
192+ if found. contains ( "bpf_map_create" ) && found. contains ( "bpf_map_alloc_security" ) {
193+ return Err ( anyhow ! (
194+ "Conflicting function definitions for security_bpf_map_create"
195+ ) ) ;
196+ } else if !found. contains ( "bpf_map_create" ) && !found. contains ( "bpf_map_alloc_security" ) {
197+ return Err ( anyhow ! (
198+ "No function definition found for security_bpf_map_create"
199+ ) ) ;
200+ }
201+
202+ Ok ( found)
203+ }
204+
205+ fn export_features_to_header ( features : HashSet < String > ) -> Result < ( ) > {
206+ let mut f = fs:: File :: create ( "include/vmlinux_features.h" ) ?;
207+
208+ writeln ! ( f, "// Auto-generated header from build.rs" ) ?;
209+
210+ for flag in features {
211+ let macro_name = flag. to_uppercase ( ) ;
212+ writeln ! ( f, "#define HAS_{}" , macro_name) ?;
213+ }
214+
215+ Ok ( ( ) )
216+ }
217+
201218fn main ( ) -> Result < ( ) > {
202219 let out_path = PathBuf :: from ( env:: var_os ( "OUT_DIR" ) . context ( "OUT_DIR must be set" ) ?) ;
203220 extract_libbpf_headers_to_disk ( & out_path) ?;
221+ // Create vmlinux and do feature detection based on it
222+ if !Path :: new ( VMLINUX_PATH ) . exists ( ) {
223+ generate_vmlinux ( ) ?;
224+ let features = detect_vmlinux_features ( ) ?;
225+ export_features_to_header ( features) ?;
226+ }
204227 // Build common
205228 build (
206229 & out_path,
@@ -213,16 +236,10 @@ fn main() -> Result<()> {
213236 "seabee_maps.h" ,
214237 "seabee_utils.h" ,
215238 ] ,
216- None ,
217239 ) ?;
218240 // Build bpf code
219- build (
220- & out_path,
221- "src/seabee" ,
222- vec ! [ "seabee_log.h" ] ,
223- Some ( & [ KernelVersion :: new ( 6 , 1 , 0 ) , KernelVersion :: new ( 6 , 9 , 0 ) ] ) ,
224- ) ?;
225- build ( & out_path, "src/kernel_api" , vec ! [ ] , None ) ?;
226- build ( & out_path, "src/tests" , vec ! [ ] , None ) ?;
241+ build ( & out_path, "src/seabee" , vec ! [ "seabee_log.h" ] ) ?;
242+ build ( & out_path, "src/kernel_api" , vec ! [ ] ) ?;
243+ build ( & out_path, "src/tests" , vec ! [ ] ) ?;
227244 Ok ( ( ) )
228245}
0 commit comments