bpf_loader_lib/skeleton/
builder.rs

1//!  SPDX-License-Identifier: MIT
2//!
3//! Copyright (c) 2023, eunomia-bpf
4//! All rights reserved.
5//!
6
7use std::{
8    collections::HashMap,
9    ffi::{c_void, CStr},
10    os::{
11        raw::c_char,
12        unix::prelude::{OsStrExt, PermissionsExt},
13    },
14    path::PathBuf,
15    ptr::NonNull,
16};
17
18use crate::{
19    btf_container::BtfContainer,
20    elf_container::ElfContainer,
21    helper::btf::create_elf_with_btf_section,
22    meta::{ComposedObject, EunomiaObjectMeta, RunnerConfig},
23    skeleton::{BTF_PATH_ENV_NAME, VMLINUX_BTF_PATH},
24};
25use anyhow::{anyhow, bail, Result};
26use bpf_compatible_rs::get_current_system_btf_file;
27use libbpf_rs::{
28    libbpf_sys::{
29        self, bpf_map__name, bpf_map__value_size, bpf_object__btf, bpf_object__next_map,
30        btf__get_raw_data,
31    },
32    ObjectBuilder, OpenObject,
33};
34
35use super::preload::PreLoadBpfSkeleton;
36
37/// Builder of BpfSkeleton
38pub struct BpfSkeletonBuilder<'a> {
39    btf_archive_path: Option<&'a str>,
40    object_meta: &'a EunomiaObjectMeta,
41    bpf_object: &'a [u8],
42    runner_config: Option<RunnerConfig>,
43}
44
45impl<'a> BpfSkeletonBuilder<'a> {
46    /// Create a builder using provided meta and bpj_object
47    /// btf_archive_path - Path to the root of kernel btf archives, if not provided, will try to use env var `BTF_FILEP_PATH` and /sys/kernel/btf/vmlinux
48    pub fn from_object_meta_and_object_buffer(
49        meta: &'a EunomiaObjectMeta,
50        bpf_object: &'a [u8],
51        btf_archive_path: Option<&'a str>,
52    ) -> Self {
53        Self {
54            btf_archive_path,
55            object_meta: meta,
56            bpf_object,
57            runner_config: None,
58        }
59    }
60    /// Create a builder from the json package
61    /// btf_archive_path - Path to the root of kernel btf archives, if not provided, will try to use env var `BTF_FILEP_PATH` and /sys/kernel/btf/vmlinux
62    pub fn from_json_package(
63        package: &'a ComposedObject,
64        btf_archive_path: Option<&'a str>,
65    ) -> Self {
66        Self::from_object_meta_and_object_buffer(
67            &package.meta,
68            &package.bpf_object,
69            btf_archive_path,
70        )
71    }
72    /// Set the runner_config of this bpf program
73    pub fn set_runner_config(self, cfg: RunnerConfig) -> Self {
74        Self {
75            runner_config: Some(cfg),
76            ..self
77        }
78    }
79    /// Build (open) the skeleton
80    pub fn build(self) -> Result<PreLoadBpfSkeleton> {
81        let mut open_bpts = ObjectBuilder::default()
82            .opts(self.object_meta.bpf_skel.obj_name.as_bytes().as_ptr() as *const c_char);
83        // Why we put path_holder here? to keep its iveness until this function returns, so that we can safely use the pointers to the underlying data in bpf_object_openopts
84        let path_holder = if let Some(base_path) = self.btf_archive_path.as_ref() {
85            let path = get_current_system_btf_file(PathBuf::from(base_path).as_path())?;
86            if !path.exists() {
87                bail!("BTF file not found for current system: {}", path.display());
88            }
89            Some(path)
90        } else {
91            None
92        };
93        // Ditto
94        let env_btf_file_path = std::env::var_os(BTF_PATH_ENV_NAME);
95
96        let vmlinux_btf_exists = if PathBuf::from(VMLINUX_BTF_PATH).exists() {
97            match std::fs::metadata(VMLINUX_BTF_PATH) {
98                Ok(meta) => {
99                    // Tests if we have S_IRUSR permission
100                    meta.permissions().mode() & 0o0400 != 0
101                }
102                Err(e) => {
103                    log::info!("Failed to get metadata of {}: {}", VMLINUX_BTF_PATH, e);
104                    false
105                }
106            }
107        } else {
108            false
109        };
110        if path_holder.is_some() && !vmlinux_btf_exists {
111            // We have to manually modify open_opts and open bpf_object, because libbpf-rs currently doesn't support customizing this..
112
113            // SAFETY: path_holder will lives until this function returns
114            open_bpts.btf_custom_path = path_holder
115                .as_ref()
116                .unwrap()
117                .as_os_str()
118                .as_bytes()
119                .as_ptr() as *const _;
120        } else if let Some(env_btf) = env_btf_file_path.as_ref() {
121            // SAFETY: env_btf_file_path will live until this function returns
122            open_bpts.btf_custom_path = env_btf.as_bytes().as_ptr() as *const _;
123        } else if !vmlinux_btf_exists {
124            bail!("All ways tried to find vmlinux BTF, but not found. Please provide the vmlinux btf using env `BTF_FILE_PATH`. (Tried parameter `btf_archive_path`, {}, and {})",BTF_PATH_ENV_NAME,VMLINUX_BTF_PATH);
125        };
126        // SAFETY: FFI call. Pointers passed in will live during the call
127        let open_result = unsafe {
128            libbpf_sys::bpf_object__open_mem(
129                self.bpf_object.as_ptr() as *const c_void,
130                self.bpf_object.len() as libbpf_sys::size_t,
131                &open_bpts,
132            )
133        };
134        if open_result.is_null() {
135            bail!(
136                "Failed to open bpf object: bpf_object__open_mem returned NULL with errno={}",
137                errno::errno()
138            );
139        }
140
141        // Retrieve the btf archive from the loaded bpf_object
142        let btf = {
143            // SAFETY: This function will always succeed
144            let btf = unsafe { bpf_object__btf(open_result) };
145            if btf.is_null() {
146                bail!("Failed to get btf* from the bpf_object: {}", errno::errno());
147            }
148            // Dump the original data
149            let mut dumped_size: u32 = 0;
150            // SAFETY: It will never fault, since btf is valid
151            let raw_data = unsafe { btf__get_raw_data(btf, &mut dumped_size as *mut u32) };
152            if raw_data.is_null() {
153                bail!(
154                    "Failed to get the raw btf data from btf *: {}",
155                    errno::errno()
156                );
157            }
158            // SAFETY: btf__get_raw_data ensured that only dumped_size bytes can be used
159            // The slice will never be used once this block exits, it will be cloned in BtfContainer
160            let data =
161                unsafe { std::slice::from_raw_parts(raw_data as *const u8, dumped_size as usize) };
162            BtfContainer::new_from_binary(&create_elf_with_btf_section(data, true)?)?
163        };
164
165        let map_value_sizes = {
166            let mut sizes = HashMap::default();
167            let mut curr_map = std::ptr::null();
168            loop {
169                // SAFETY: it's always to call this, since open_result and curr_map are all valid
170                curr_map = unsafe { bpf_object__next_map(open_result, curr_map) };
171                if curr_map.is_null() {
172                    break;
173                }
174                // SAFETY: libbpf ensures that the map name is valid
175                let map_name = unsafe { CStr::from_ptr(bpf_map__name(curr_map)) }
176                    .to_str()
177                    .map_err(|e| anyhow!("Map name contains invalid character: {}", e))?;
178                // SAFETY: curr_map is valid
179                let value_size = unsafe { bpf_map__value_size(curr_map) };
180                sizes.insert(map_name.into(), value_size);
181            }
182            sizes
183        };
184        // SAFETY: The pointer won't be used by us anymore, and we also checked if it's null
185        let open_object = unsafe { OpenObject::from_ptr(NonNull::new_unchecked(open_result)) }?;
186
187        Ok(PreLoadBpfSkeleton {
188            bpf_object: open_object,
189            config_data: self.runner_config.unwrap_or_default(),
190            btf,
191            meta: self.object_meta.clone(),
192            map_value_sizes,
193            raw_elf: ElfContainer::new_from_binary(self.bpf_object)?,
194        })
195    }
196}
197
198#[cfg(test)]
199#[cfg(not(feature = "no-load-bpf-tests"))]
200mod tests {
201    use libbpf_rs::libbpf_sys::{bpf_map__fd, bpf_map__initial_value, size_t};
202
203    use crate::{
204        meta::ComposedObject, skeleton::builder::BpfSkeletonBuilder, tests::get_assets_dir,
205    };
206
207    #[test]
208    fn test_bpf_skeleton_builder_1() {
209        let package = serde_json::from_str::<ComposedObject>(
210            &std::fs::read_to_string(get_assets_dir().join("runqlat.json")).unwrap(),
211        )
212        .unwrap();
213        let preload = BpfSkeletonBuilder::from_json_package(&package, None)
214            .build()
215            .unwrap();
216        // Check the maps
217        let bpf_object = preload.bpf_object;
218        for map in package.meta.bpf_skel.maps.iter() {
219            let map_from_bpf = bpf_object.map(map.name.as_str()).unwrap();
220            println!("{:?}", map_from_bpf);
221        }
222        // Check the progs
223        for prog in package.meta.bpf_skel.progs.iter() {
224            let prog_from_bpf = bpf_object.prog(prog.name.as_str()).unwrap();
225            println!("{:?}", prog_from_bpf);
226        }
227        {
228            let s = bpf_object.load().unwrap();
229            for map in s.maps_iter() {
230                let mptr = map.as_libbpf_bpf_map_ptr().unwrap();
231                let mut s: size_t = 0;
232                let ptr = unsafe { bpf_map__initial_value(mptr.as_ptr(), &mut s as *mut _) };
233                println!(
234                    "{} key size {} value size {} max ent {} mmapedptr {:?} mmaped size {} fd {}",
235                    map.name(),
236                    map.key_size(),
237                    map.value_size(),
238                    map.info().unwrap().info.max_entries,
239                    ptr,
240                    s,
241                    unsafe { bpf_map__fd(mptr.as_ptr()) }
242                );
243            }
244        }
245    }
246}