1 use core::ffi::c_void;
2 use std::alloc::alloc_zeroed;
3 use std::alloc::dealloc;
4 use std::alloc::Layout;
5 use std::ffi::CString;
6 use std::mem::size_of;
7 use std::mem::MaybeUninit;
8 use std::os::raw::c_char;
9 use std::os::raw::c_ulong;
10 use std::ptr;
11 use std::ptr::addr_of;
12 use std::ptr::NonNull;
13
14 use libbpf_sys::bpf_link;
15 use libbpf_sys::bpf_map;
16 use libbpf_sys::bpf_map_skeleton;
17 use libbpf_sys::bpf_object;
18 use libbpf_sys::bpf_object_skeleton;
19 use libbpf_sys::bpf_prog_skeleton;
20 use libbpf_sys::bpf_program;
21
22 use crate::error::IntoError as _;
23 use crate::util;
24 use crate::AsRawLibbpf;
25 use crate::Error;
26 use crate::Object;
27 use crate::ObjectBuilder;
28 use crate::OpenObject;
29 use crate::Result;
30
31 #[derive(Debug)]
32 struct MapSkelConfig {
33 name: String,
34 p: Box<*mut bpf_map>,
35 mmaped: Option<Box<*mut c_void>>,
36 }
37
38 #[derive(Debug)]
39 struct ProgSkelConfig {
40 name: String,
41 p: Box<*mut bpf_program>,
42 link: Box<*mut bpf_link>,
43 }
44
45 #[allow(missing_docs)]
46 #[derive(Debug)]
47 pub struct ObjectSkeletonConfigBuilder<'dat> {
48 data: &'dat [u8],
49 p: Box<*mut bpf_object>,
50 name: Option<String>,
51 maps: Vec<MapSkelConfig>,
52 progs: Vec<ProgSkelConfig>,
53 }
54
str_to_cstring_and_pool(s: &str, pool: &mut Vec<CString>) -> Result<*const c_char>55 fn str_to_cstring_and_pool(s: &str, pool: &mut Vec<CString>) -> Result<*const c_char> {
56 let cname = util::str_to_cstring(s)?;
57 let p = cname.as_ptr();
58 pool.push(cname);
59
60 Ok(p)
61 }
62
63 impl<'dat> ObjectSkeletonConfigBuilder<'dat> {
64 /// Construct a new instance
65 ///
66 /// `object_data` is the contents of the `.o` from clang
67 ///
68 /// `p` is a reference to the pointer where `libbpf_sys::bpf_object` should be
69 /// stored/retrieved
new(object_data: &'dat [u8]) -> Self70 pub fn new(object_data: &'dat [u8]) -> Self {
71 Self {
72 data: object_data,
73 p: Box::new(ptr::null_mut()),
74 name: None,
75 maps: Vec::new(),
76 progs: Vec::new(),
77 }
78 }
79
80 #[allow(missing_docs)]
name<T: AsRef<str>>(&mut self, name: T) -> &mut Self81 pub fn name<T: AsRef<str>>(&mut self, name: T) -> &mut Self {
82 self.name = Some(name.as_ref().to_string());
83 self
84 }
85
86 /// Adds a map to the config
87 ///
88 /// Set `mmaped` to `true` if the map is mmap'able to userspace
map<T: AsRef<str>>(&mut self, name: T, mmaped: bool) -> &mut Self89 pub fn map<T: AsRef<str>>(&mut self, name: T, mmaped: bool) -> &mut Self {
90 let m = if mmaped {
91 Some(Box::new(ptr::null_mut()))
92 } else {
93 None
94 };
95
96 self.maps.push(MapSkelConfig {
97 name: name.as_ref().to_string(),
98 p: Box::new(ptr::null_mut()),
99 mmaped: m,
100 });
101
102 self
103 }
104
105 /// Adds a prog to the config
prog<T: AsRef<str>>(&mut self, name: T) -> &mut Self106 pub fn prog<T: AsRef<str>>(&mut self, name: T) -> &mut Self {
107 self.progs.push(ProgSkelConfig {
108 name: name.as_ref().to_string(),
109 p: Box::new(ptr::null_mut()),
110 link: Box::new(ptr::null_mut()),
111 });
112
113 self
114 }
115
build_maps( maps: &mut [MapSkelConfig], s: &mut bpf_object_skeleton, string_pool: &mut Vec<CString>, ) -> Option<Layout>116 fn build_maps(
117 maps: &mut [MapSkelConfig],
118 s: &mut bpf_object_skeleton,
119 string_pool: &mut Vec<CString>,
120 ) -> Option<Layout> {
121 if maps.is_empty() {
122 return None;
123 }
124
125 s.map_cnt = maps.len() as i32;
126 s.map_skel_sz = size_of::<bpf_map_skeleton>() as i32;
127
128 let layout = Layout::array::<bpf_map_skeleton>(maps.len())
129 .expect("Failed to allocate memory for maps skeleton");
130
131 unsafe {
132 s.maps = alloc_zeroed(layout) as *mut bpf_map_skeleton;
133 for (i, map) in maps.iter_mut().enumerate() {
134 let current_map = s.maps.add(i);
135
136 // Opt to panic on error here. We've already allocated memory and we'd rather not
137 // leak. Extremely unlikely to have invalid unicode anyways.
138 (*current_map).name = str_to_cstring_and_pool(&map.name, string_pool)
139 .expect("Invalid unicode in map name");
140 (*current_map).map = &mut *map.p;
141 (*current_map).mmaped = if let Some(ref mut mmaped) = map.mmaped {
142 &mut **mmaped
143 } else {
144 ptr::null_mut()
145 };
146 }
147 }
148
149 Some(layout)
150 }
151
build_progs( progs: &mut [ProgSkelConfig], s: &mut bpf_object_skeleton, string_pool: &mut Vec<CString>, ) -> Option<Layout>152 fn build_progs(
153 progs: &mut [ProgSkelConfig],
154 s: &mut bpf_object_skeleton,
155 string_pool: &mut Vec<CString>,
156 ) -> Option<Layout> {
157 if progs.is_empty() {
158 return None;
159 }
160
161 s.prog_cnt = progs.len() as i32;
162 s.prog_skel_sz = size_of::<bpf_prog_skeleton>() as i32;
163
164 let layout = Layout::array::<bpf_prog_skeleton>(progs.len())
165 .expect("Failed to allocate memory for progs skeleton");
166
167 unsafe {
168 s.progs = alloc_zeroed(layout) as *mut bpf_prog_skeleton;
169 for (i, prog) in progs.iter_mut().enumerate() {
170 let current_prog = s.progs.add(i);
171
172 // See above for `expect()` rationale
173 (*current_prog).name = str_to_cstring_and_pool(&prog.name, string_pool)
174 .expect("Invalid unicode in prog name");
175 (*current_prog).prog = &mut *prog.p;
176 (*current_prog).link = &mut *prog.link;
177 }
178 }
179
180 Some(layout)
181 }
182
183 #[allow(missing_docs)]
build(mut self) -> Result<ObjectSkeletonConfig<'dat>>184 pub fn build(mut self) -> Result<ObjectSkeletonConfig<'dat>> {
185 // Holds `CString`s alive so pointers to them stay valid
186 let mut string_pool = Vec::new();
187
188 let mut s = libbpf_sys::bpf_object_skeleton {
189 sz: size_of::<bpf_object_skeleton>() as c_ulong,
190 ..Default::default()
191 };
192
193 if let Some(ref n) = self.name {
194 s.name = str_to_cstring_and_pool(n, &mut string_pool)?;
195 }
196
197 // libbpf_sys will use it as const despite the signature
198 s.data = self.data.as_ptr() as *mut c_void;
199 s.data_sz = self.data.len() as c_ulong;
200
201 // Give s ownership over the box
202 s.obj = Box::into_raw(self.p);
203
204 let maps_layout = Self::build_maps(&mut self.maps, &mut s, &mut string_pool);
205 let progs_layout = Self::build_progs(&mut self.progs, &mut s, &mut string_pool);
206
207 Ok(ObjectSkeletonConfig {
208 inner: s,
209 maps: self.maps,
210 progs: self.progs,
211 maps_layout,
212 progs_layout,
213 _data: self.data,
214 _string_pool: string_pool,
215 })
216 }
217 }
218
219 /// Helper struct that wraps a `libbpf_sys::bpf_object_skeleton`.
220 ///
221 /// This struct will:
222 /// * ensure lifetimes are valid for dependencies (pointers, data buffer)
223 /// * free any allocated memory on drop
224 ///
225 /// This struct can be moved around at will. Upon drop, all allocated resources will be freed
226 #[derive(Debug)]
227 pub struct ObjectSkeletonConfig<'dat> {
228 inner: bpf_object_skeleton,
229 maps: Vec<MapSkelConfig>,
230 progs: Vec<ProgSkelConfig>,
231 /// Layout necessary to `dealloc` memory
232 maps_layout: Option<Layout>,
233 /// Same as above
234 progs_layout: Option<Layout>,
235 /// Hold this reference so that compiler guarantees buffer lives as long as us
236 _data: &'dat [u8],
237 /// Hold strings alive so pointers to them stay valid
238 _string_pool: Vec<CString>,
239 }
240
241 impl ObjectSkeletonConfig<'_> {
242 /// Returns the `mmaped` pointer for a map at the specified `index`.
243 ///
244 /// The index is determined by the order in which the map was passed to
245 /// `ObjectSkeletonConfigBuilder::map`. Index starts at 0.
246 ///
247 /// Warning: the returned pointer is only valid while the `ObjectSkeletonConfig` is alive.
map_mmap_ptr(&self, index: usize) -> Result<*mut c_void>248 pub fn map_mmap_ptr(&self, index: usize) -> Result<*mut c_void> {
249 if index >= self.maps.len() {
250 return Err(Error::with_invalid_data(format!(
251 "Invalid map index: {index}"
252 )));
253 }
254
255 let p = self.maps[index]
256 .mmaped
257 .as_ref()
258 .ok_or_invalid_data(|| "Map does not have mmaped ptr")?;
259 Ok(**p)
260 }
261
262 /// Returns the link pointer for a prog at the specified `index`.
263 ///
264 /// The index is determined by the order in which the prog was passed to
265 /// `ObjectSkeletonConfigBuilder::prog`. Index starts at 0.
266 ///
267 /// Warning: the returned pointer is only valid while the `ObjectSkeletonConfig` is alive.
prog_link_ptr(&self, index: usize) -> Result<*mut bpf_link>268 pub fn prog_link_ptr(&self, index: usize) -> Result<*mut bpf_link> {
269 if index >= self.progs.len() {
270 return Err(Error::with_invalid_data(format!(
271 "Invalid prog index: {index}"
272 )));
273 }
274
275 Ok(*self.progs[index].link)
276 }
277 }
278
279 impl AsRawLibbpf for ObjectSkeletonConfig<'_> {
280 type LibbpfType = libbpf_sys::bpf_object_skeleton;
281
282 /// Retrieve the underlying [`libbpf_sys::bpf_object_skeleton`].
as_libbpf_object(&self) -> NonNull<Self::LibbpfType>283 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
284 // SAFETY: A reference is always a valid pointer.
285 unsafe { NonNull::new_unchecked(addr_of!(self.inner).cast_mut()) }
286 }
287 }
288
289 impl Drop for ObjectSkeletonConfig<'_> {
290 // Note we do *not* run `libbpf_sys::bpf_object__destroy_skeleton` here.
291 //
292 // Couple reasons:
293 //
294 // 1) We did not allocate `libbpf_sys::bpf_object_skeleton` on the heap and
295 // `libbpf_sys::bpf_object__destroy_skeleton` will try to free from heap
296 //
297 // 2) `libbpf_object_skeleton` assumes it "owns" the object and everything inside it.
298 // libbpf-cargo's generated skeleton instead gives ownership of the object to
299 // libbpf-rs::*Object. The destructors in libbpf-rs::*Object will know when and how to do
300 // cleanup.
drop(&mut self)301 fn drop(&mut self) {
302 assert_eq!(self.maps_layout.is_none(), self.inner.maps.is_null());
303 assert_eq!(self.progs_layout.is_none(), self.inner.progs.is_null());
304
305 if let Some(layout) = self.maps_layout {
306 unsafe {
307 dealloc(self.inner.maps as _, layout);
308 }
309 }
310
311 if let Some(layout) = self.progs_layout {
312 unsafe {
313 dealloc(self.inner.progs as _, layout);
314 }
315 }
316
317 let _ = unsafe { Box::from_raw(self.inner.obj) };
318 }
319 }
320
321 /// A trait for skeleton builder.
322 pub trait SkelBuilder<'obj> {
323 /// Define that when BPF object is opened, the returned type should implement the [`OpenSkel`]
324 /// trait
325 type Output: OpenSkel<'obj>;
326
327 /// Open eBPF object and return [`OpenSkel`]
open(self, object: &'obj mut MaybeUninit<OpenObject>) -> Result<Self::Output>328 fn open(self, object: &'obj mut MaybeUninit<OpenObject>) -> Result<Self::Output>;
329
330 /// Open eBPF object with [`libbpf_sys::bpf_object_open_opts`] and return [`OpenSkel`]
open_opts( self, open_opts: libbpf_sys::bpf_object_open_opts, object: &'obj mut MaybeUninit<OpenObject>, ) -> Result<Self::Output>331 fn open_opts(
332 self,
333 open_opts: libbpf_sys::bpf_object_open_opts,
334 object: &'obj mut MaybeUninit<OpenObject>,
335 ) -> Result<Self::Output>;
336
337 /// Get a reference to [`ObjectBuilder`]
object_builder(&self) -> &ObjectBuilder338 fn object_builder(&self) -> &ObjectBuilder;
339
340 /// Get a mutable reference to [`ObjectBuilder`]
object_builder_mut(&mut self) -> &mut ObjectBuilder341 fn object_builder_mut(&mut self) -> &mut ObjectBuilder;
342 }
343
344 /// A trait for opened skeleton.
345 ///
346 /// In addition to the methods defined in this trait, skeletons that implement this trait will also
347 /// have bespoke implementations of a few additional methods to facilitate access to global
348 /// variables of the BPF program. These methods will be named `bss()`, `data()`, and `rodata()`.
349 /// Each corresponds to the variables stored in the BPF ELF program section of the same name.
350 /// However if your BPF program lacks one of these sections the corresponding rust method will not
351 /// be generated.
352 ///
353 /// The type of the value returned by each of these methods will be specific to your BPF program.
354 /// A common convention is to define a single global variable in the BPF program with a struct type
355 /// containing a field for each configuration parameter <sup>\[[source]\]</sup>. libbpf-rs
356 /// auto-generates this pattern for you without you having to define such a struct type in your BPF
357 /// program. It does this by examining each of the global variables in your BPF program's `.bss`,
358 /// `.data`, and `.rodata` sections and then creating Rust struct types. Since these struct types
359 /// are specific to the layout of your BPF program, they are not documented in this crate. However
360 /// you can see documentation for them by running `cargo doc` in your own project and looking at
361 /// the `imp` module. You can also view their implementation by looking at the generated skeleton
362 /// rust source file. The use of these methods can also be seen in the examples 'capable',
363 /// 'runqslower', and 'tproxy'.
364 ///
365 /// If you ever doubt whether libbpf-rs has placed a particular variable in the correct struct
366 /// type, you can see which section each global variable is stored in by examining the output of
367 /// the following command (after a successful build):
368 ///
369 /// ```sh
370 /// bpf-objdump --syms ./target/bpf/*.bpf.o
371 /// ```
372 ///
373 /// [source]: https://nakryiko.com/posts/bcc-to-libbpf-howto-guide/#application-configuration
374 pub trait OpenSkel<'obj> {
375 /// Define that when BPF object is loaded, the returned type should implement the [`Skel`] trait
376 type Output: Skel<'obj>;
377
378 /// Load BPF object and return [`Skel`].
load(self) -> Result<Self::Output>379 fn load(self) -> Result<Self::Output>;
380
381 /// Get a reference to [`OpenObject`].
open_object(&self) -> &OpenObject382 fn open_object(&self) -> &OpenObject;
383
384 /// Get a mutable reference to [`OpenObject`].
open_object_mut(&mut self) -> &mut OpenObject385 fn open_object_mut(&mut self) -> &mut OpenObject;
386 }
387
388 /// A trait for loaded skeleton.
389 pub trait Skel<'obj> {
390 /// Attach BPF object.
attach(&mut self) -> Result<()>391 fn attach(&mut self) -> Result<()> {
392 unimplemented!()
393 }
394 /// Get a reference to [`Object`].
object(&self) -> &Object395 fn object(&self) -> &Object;
396
397 /// Get a mutable reference to [`Object`].
object_mut(&mut self) -> &mut Object398 fn object_mut(&mut self) -> &mut Object;
399 }
400