1use core::ffi::c_void;
2use std::alloc::alloc_zeroed;
3use std::alloc::dealloc;
4use std::alloc::Layout;
5use std::ffi::CString;
6use std::mem::size_of;
7use std::mem::MaybeUninit;
8use std::os::raw::c_char;
9use std::os::raw::c_ulong;
10use std::ptr;
11use std::ptr::addr_of;
12use std::ptr::NonNull;
13
14use libbpf_sys::bpf_link;
15use libbpf_sys::bpf_map;
16use libbpf_sys::bpf_map_skeleton;
17use libbpf_sys::bpf_object;
18use libbpf_sys::bpf_object_skeleton;
19use libbpf_sys::bpf_prog_skeleton;
20use libbpf_sys::bpf_program;
21
22use crate::error::IntoError as _;
23use crate::util;
24use crate::AsRawLibbpf;
25use crate::Error;
26use crate::Object;
27use crate::ObjectBuilder;
28use crate::OpenObject;
29use crate::Result;
30
31#[derive(Debug)]
32struct MapSkelConfig {
33 name: String,
34 p: Box<*mut bpf_map>,
35 mmaped: Option<Box<*mut c_void>>,
36}
37
38#[derive(Debug)]
39struct 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)]
47pub 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
55fn 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
63impl<'dat> ObjectSkeletonConfigBuilder<'dat> {
64 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)]
81 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 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 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
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 (*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
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 (*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)]
184 pub fn build(mut self) -> Result<ObjectSkeletonConfig<'dat>> {
185 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 s.data = self.data.as_ptr() as *mut c_void;
199 s.data_sz = self.data.len() as c_ulong;
200
201 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#[derive(Debug)]
227pub struct ObjectSkeletonConfig<'dat> {
228 inner: bpf_object_skeleton,
229 maps: Vec<MapSkelConfig>,
230 progs: Vec<ProgSkelConfig>,
231 maps_layout: Option<Layout>,
233 progs_layout: Option<Layout>,
235 _data: &'dat [u8],
237 _string_pool: Vec<CString>,
239}
240
241impl ObjectSkeletonConfig<'_> {
242 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 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
279impl AsRawLibbpf for ObjectSkeletonConfig<'_> {
280 type LibbpfType = libbpf_sys::bpf_object_skeleton;
281
282 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
284 unsafe { NonNull::new_unchecked(addr_of!(self.inner).cast_mut()) }
286 }
287}
288
289impl Drop for ObjectSkeletonConfig<'_> {
290 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
321pub trait SkelBuilder<'obj> {
323 type Output: OpenSkel<'obj>;
326
327 fn open(self, object: &'obj mut MaybeUninit<OpenObject>) -> Result<Self::Output>;
329
330 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 fn object_builder(&self) -> &ObjectBuilder;
339
340 fn object_builder_mut(&mut self) -> &mut ObjectBuilder;
342}
343
344pub trait OpenSkel<'obj> {
375 type Output: Skel<'obj>;
377
378 fn load(self) -> Result<Self::Output>;
380
381 fn open_object(&self) -> &OpenObject;
383
384 fn open_object_mut(&mut self) -> &mut OpenObject;
386}
387
388pub trait Skel<'obj> {
390 fn attach(&mut self) -> Result<()> {
392 unimplemented!()
393 }
394 fn object(&self) -> &Object;
396
397 fn object_mut(&mut self) -> &mut Object;
399}