use std::{
ffi::OsStr,
fmt::{self, Write},
io,
os::fd::AsFd as _,
path::{Path, PathBuf},
};
use aya_obj::generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE};
use thiserror::Error;
use crate::{
VerifierLogLevel,
programs::{
FdLink, LinkError, ProgramData, ProgramError, ProgramType, define_link_wrapper,
impl_try_into_fdlink, load_program,
perf_attach::{PerfLinkIdInner, PerfLinkInner},
probe::{Probe, ProbeKind, attach},
},
sys::bpf_link_get_info_by_fd,
};
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_KPROBE")]
pub struct KProbe {
pub(crate) data: ProgramData<KProbeLink>,
pub(crate) kind: ProbeKind,
}
impl KProbe {
pub const PROGRAM_TYPE: ProgramType = ProgramType::KProbe;
pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_KPROBE, &mut self.data)
}
pub const fn kind(&self) -> ProbeKind {
self.kind
}
pub fn attach<T: AsRef<OsStr>>(
&mut self,
fn_name: T,
offset: u64,
) -> Result<KProbeLinkId, ProgramError> {
let Self { data, kind } = self;
attach::<Self, _>(
data,
*kind,
fn_name.as_ref(),
offset,
None, None, )
}
pub fn from_pin<P: AsRef<Path>>(path: P, kind: ProbeKind) -> Result<Self, ProgramError> {
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
Ok(Self { data, kind })
}
}
impl Probe for KProbe {
const PMU: &'static str = "kprobe";
type Error = KProbeError;
fn file_error(filename: PathBuf, io_error: io::Error) -> Self::Error {
KProbeError::FileError { filename, io_error }
}
fn write_offset<W: Write>(w: &mut W, kind: ProbeKind, offset: u64) -> fmt::Result {
match kind {
ProbeKind::Entry => write!(w, "+{offset}"),
ProbeKind::Return => Ok(()),
}
}
}
define_link_wrapper!(
KProbeLink,
KProbeLinkId,
PerfLinkInner,
PerfLinkIdInner,
KProbe,
);
#[derive(Debug, Error)]
pub enum KProbeError {
#[error("`{filename}`")]
FileError {
filename: PathBuf,
#[source]
io_error: io::Error,
},
}
impl_try_into_fdlink!(KProbeLink, PerfLinkInner);
impl TryFrom<FdLink> for KProbeLink {
type Error = LinkError;
fn try_from(fd_link: FdLink) -> Result<Self, Self::Error> {
let info = bpf_link_get_info_by_fd(fd_link.fd.as_fd())?;
if info.type_ == (bpf_link_type::BPF_LINK_TYPE_KPROBE_MULTI as u32) {
return Ok(Self::new(PerfLinkInner::Fd(fd_link)));
}
Err(LinkError::InvalidLink)
}
}