use core::ffi::c_void;
use std::alloc::alloc_zeroed;
use std::alloc::dealloc;
use std::alloc::Layout;
use std::ffi::CString;
use std::mem::size_of;
use std::mem::MaybeUninit;
use std::os::raw::c_char;
use std::os::raw::c_ulong;
use std::ptr;
use std::ptr::addr_of;
use std::ptr::NonNull;
use libbpf_sys::bpf_link;
use libbpf_sys::bpf_map;
use libbpf_sys::bpf_map_skeleton;
use libbpf_sys::bpf_object;
use libbpf_sys::bpf_object_skeleton;
use libbpf_sys::bpf_prog_skeleton;
use libbpf_sys::bpf_program;
use crate::error::IntoError as _;
use crate::util;
use crate::AsRawLibbpf;
use crate::Error;
use crate::Object;
use crate::ObjectBuilder;
use crate::OpenObject;
use crate::Result;
#[derive(Debug)]
struct MapSkelConfig {
name: String,
map: Box<*mut bpf_map>,
mmaped: Option<Box<*mut c_void>>,
}
#[derive(Debug)]
struct ProgSkelConfig {
name: String,
prog: Box<*mut bpf_program>,
link: Box<*mut bpf_link>,
}
#[expect(missing_docs)]
#[derive(Debug)]
pub struct ObjectSkeletonConfigBuilder<'dat> {
data: &'dat [u8],
obj: Box<*mut bpf_object>,
name: Option<String>,
maps: Vec<MapSkelConfig>,
progs: Vec<ProgSkelConfig>,
}
fn str_to_cstring_and_pool(s: &str, pool: &mut Vec<CString>) -> Result<*const c_char> {
let cname = util::str_to_cstring(s)?;
let p = cname.as_ptr();
pool.push(cname);
Ok(p)
}
impl<'dat> ObjectSkeletonConfigBuilder<'dat> {
pub fn new(object_data: &'dat [u8]) -> Self {
Self {
data: object_data,
obj: Box::new(ptr::null_mut()),
name: None,
maps: Vec::new(),
progs: Vec::new(),
}
}
#[expect(missing_docs)]
pub fn name<T: AsRef<str>>(&mut self, name: T) -> &mut Self {
self.name = Some(name.as_ref().to_string());
self
}
pub fn map<T: AsRef<str>>(&mut self, name: T, mmaped: bool) -> &mut Self {
let m = if mmaped {
Some(Box::new(ptr::null_mut()))
} else {
None
};
self.maps.push(MapSkelConfig {
name: name.as_ref().to_string(),
map: Box::new(ptr::null_mut()),
mmaped: m,
});
self
}
pub fn prog<T: AsRef<str>>(&mut self, name: T) -> &mut Self {
self.progs.push(ProgSkelConfig {
name: name.as_ref().to_string(),
prog: Box::new(ptr::null_mut()),
link: Box::new(ptr::null_mut()),
});
self
}
fn build_maps(
maps: &mut [MapSkelConfig],
s: &mut bpf_object_skeleton,
string_pool: &mut Vec<CString>,
) -> Option<Layout> {
if maps.is_empty() {
return None;
}
s.map_cnt = maps.len() as i32;
s.map_skel_sz = size_of::<bpf_map_skeleton>() as i32;
let layout = Layout::array::<bpf_map_skeleton>(maps.len())
.expect("Failed to allocate memory for maps skeleton");
unsafe {
s.maps = alloc_zeroed(layout) as *mut bpf_map_skeleton;
for (i, map) in maps.iter_mut().enumerate() {
let current_map = s.maps.add(i);
(*current_map).name = str_to_cstring_and_pool(&map.name, string_pool)
.expect("Invalid unicode in map name");
(*current_map).map = &mut *map.map;
(*current_map).mmaped = if let Some(ref mut mmaped) = map.mmaped {
&mut **mmaped
} else {
ptr::null_mut()
};
}
}
Some(layout)
}
fn build_progs(
progs: &mut [ProgSkelConfig],
s: &mut bpf_object_skeleton,
string_pool: &mut Vec<CString>,
) -> Option<Layout> {
if progs.is_empty() {
return None;
}
s.prog_cnt = progs.len() as i32;
s.prog_skel_sz = size_of::<bpf_prog_skeleton>() as i32;
let layout = Layout::array::<bpf_prog_skeleton>(progs.len())
.expect("Failed to allocate memory for progs skeleton");
unsafe {
s.progs = alloc_zeroed(layout) as *mut bpf_prog_skeleton;
for (i, prog) in progs.iter_mut().enumerate() {
let current_prog = s.progs.add(i);
(*current_prog).name = str_to_cstring_and_pool(&prog.name, string_pool)
.expect("Invalid unicode in prog name");
(*current_prog).prog = &mut *prog.prog;
(*current_prog).link = &mut *prog.link;
}
}
Some(layout)
}
#[expect(missing_docs)]
pub fn build(mut self) -> Result<ObjectSkeletonConfig<'dat>> {
let mut string_pool = Vec::new();
let mut s = libbpf_sys::bpf_object_skeleton {
sz: size_of::<bpf_object_skeleton>() as c_ulong,
..Default::default()
};
if let Some(ref n) = self.name {
s.name = str_to_cstring_and_pool(n, &mut string_pool)?;
}
s.data = self.data.as_ptr() as *mut c_void;
s.data_sz = self.data.len() as c_ulong;
s.obj = Box::into_raw(self.obj);
let maps_layout = Self::build_maps(&mut self.maps, &mut s, &mut string_pool);
let progs_layout = Self::build_progs(&mut self.progs, &mut s, &mut string_pool);
Ok(ObjectSkeletonConfig {
inner: s,
maps: self.maps,
progs: self.progs,
maps_layout,
progs_layout,
_data: self.data,
_string_pool: string_pool,
})
}
}
#[derive(Debug)]
pub struct ObjectSkeletonConfig<'dat> {
inner: bpf_object_skeleton,
maps: Vec<MapSkelConfig>,
progs: Vec<ProgSkelConfig>,
maps_layout: Option<Layout>,
progs_layout: Option<Layout>,
_data: &'dat [u8],
_string_pool: Vec<CString>,
}
impl ObjectSkeletonConfig<'_> {
pub fn map_mmap_ptr(&self, index: usize) -> Result<*mut c_void> {
if index >= self.maps.len() {
return Err(Error::with_invalid_data(format!(
"Invalid map index: {index}"
)));
}
let p = self.maps[index]
.mmaped
.as_ref()
.ok_or_invalid_data(|| "Map does not have mmaped ptr")?;
Ok(**p)
}
pub fn prog_link_ptr(&self, index: usize) -> Result<*mut bpf_link> {
if index >= self.progs.len() {
return Err(Error::with_invalid_data(format!(
"Invalid prog index: {index}"
)));
}
Ok(*self.progs[index].link)
}
}
impl AsRawLibbpf for ObjectSkeletonConfig<'_> {
type LibbpfType = libbpf_sys::bpf_object_skeleton;
fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
unsafe { NonNull::new_unchecked(addr_of!(self.inner).cast_mut()) }
}
}
impl Drop for ObjectSkeletonConfig<'_> {
fn drop(&mut self) {
assert_eq!(self.maps_layout.is_none(), self.inner.maps.is_null());
assert_eq!(self.progs_layout.is_none(), self.inner.progs.is_null());
if let Some(layout) = self.maps_layout {
unsafe {
dealloc(self.inner.maps as _, layout);
}
}
if let Some(layout) = self.progs_layout {
unsafe {
dealloc(self.inner.progs as _, layout);
}
}
let () = drop(unsafe { Box::from_raw(self.inner.obj) });
}
}
pub trait SkelBuilder<'obj> {
type Output: OpenSkel<'obj>;
fn open(self, object: &'obj mut MaybeUninit<OpenObject>) -> Result<Self::Output>;
fn open_opts(
self,
open_opts: libbpf_sys::bpf_object_open_opts,
object: &'obj mut MaybeUninit<OpenObject>,
) -> Result<Self::Output>;
fn object_builder(&self) -> &ObjectBuilder;
fn object_builder_mut(&mut self) -> &mut ObjectBuilder;
}
pub trait OpenSkel<'obj> {
type Output: Skel<'obj>;
fn load(self) -> Result<Self::Output>;
fn open_object(&self) -> &OpenObject;
fn open_object_mut(&mut self) -> &mut OpenObject;
}
pub trait Skel<'obj> {
fn attach(&mut self) -> Result<()> {
unimplemented!()
}
fn object(&self) -> &Object;
fn object_mut(&mut self) -> &mut Object;
}