aya: add multi-uprobe attach support

Introduce UProbe::attach_multi with UProbeMultiAttachOptions and per-location
cookies. Require #[uprobe(multi)] programs (BPF_TRACE_UPROBE_MULTI) and guard
unsupported kernels.

Refactor symbol resolution into a single pass to batch lookups efficiently and
add integration tests covering multi-attach success and rejecting non-multi
programs
reviewable/pr1417/r1
swananan 5 days ago
parent 28ae4b9826
commit f75577c5c7

@ -29,6 +29,7 @@ pub(crate) struct UProbe {
offset: Option<u64>,
item: ItemFn,
sleepable: bool,
multi: bool,
}
impl UProbe {
@ -48,6 +49,7 @@ impl UProbe {
.transpose()
.map_err(|err| span.error(format!("failed to parse `offset` argument: {err}")))?;
let sleepable = pop_bool_arg(&mut args, "sleepable");
let multi = pop_bool_arg(&mut args, "multi");
err_on_unknown_args(&args)?;
Ok(Self {
kind,
@ -56,6 +58,7 @@ impl UProbe {
function,
offset,
sleepable,
multi,
})
}
@ -67,6 +70,7 @@ impl UProbe {
offset,
item,
sleepable,
multi,
} = self;
let ItemFn {
attrs: _,
@ -75,6 +79,9 @@ impl UProbe {
block: _,
} = item;
let mut prefix = kind.to_string();
if *multi {
prefix.push_str(".multi");
}
if *sleepable {
prefix.push_str(".s");
}

@ -227,9 +227,11 @@ pub enum ProgramSection {
KProbe,
UProbe {
sleepable: bool,
multi: bool,
},
URetProbe {
sleepable: bool,
multi: bool,
},
TracePoint,
SocketFilter,
@ -297,10 +299,38 @@ impl FromStr for ProgramSection {
Ok(match kind {
"kprobe" => Self::KProbe,
"kretprobe" => Self::KRetProbe,
"uprobe" => Self::UProbe { sleepable: false },
"uprobe.s" => Self::UProbe { sleepable: true },
"uretprobe" => Self::URetProbe { sleepable: false },
"uretprobe.s" => Self::URetProbe { sleepable: true },
"uprobe" => Self::UProbe {
sleepable: false,
multi: false,
},
"uprobe.s" => Self::UProbe {
sleepable: true,
multi: false,
},
"uprobe.multi" => Self::UProbe {
sleepable: false,
multi: true,
},
"uprobe.multi.s" => Self::UProbe {
sleepable: true,
multi: true,
},
"uretprobe" => Self::URetProbe {
sleepable: false,
multi: false,
},
"uretprobe.s" => Self::URetProbe {
sleepable: true,
multi: false,
},
"uretprobe.multi" => Self::URetProbe {
sleepable: false,
multi: true,
},
"uretprobe.multi.s" => Self::URetProbe {
sleepable: true,
multi: true,
},
"xdp" | "xdp.frags" => Self::Xdp {
frags: kind == "xdp.frags",
attach_type: match pieces.next() {

@ -11,7 +11,7 @@ use aya_obj::{
EbpfSectionKind, Features, Object, ParseError, ProgramSection,
btf::{Btf, BtfError, BtfFeatures, BtfRelocationError},
generated::{
BPF_F_SLEEPABLE, BPF_F_XDP_HAS_FRAGS,
BPF_F_SLEEPABLE, BPF_F_XDP_HAS_FRAGS, bpf_attach_type,
bpf_map_type::{self, *},
},
relocation::EbpfRelocationError,
@ -26,7 +26,7 @@ use crate::{
CgroupSockopt, CgroupSysctl, Extension, FEntry, FExit, FlowDissector, Iter, KProbe,
LircMode2, Lsm, LsmCgroup, PerfEvent, ProbeKind, Program, ProgramData, ProgramError,
RawTracePoint, SchedClassifier, SkLookup, SkMsg, SkSkb, SkSkbKind, SockOps, SocketFilter,
TracePoint, UProbe, Xdp,
TracePoint, UProbe, Xdp, uprobe::MultiCompileState,
},
sys::{
bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported,
@ -463,8 +463,8 @@ impl<'a> EbpfLoader<'a> {
}
ProgramSection::KRetProbe
| ProgramSection::KProbe
| ProgramSection::UProbe { sleepable: _ }
| ProgramSection::URetProbe { sleepable: _ }
| ProgramSection::UProbe { .. }
| ProgramSection::URetProbe { .. }
| ProgramSection::TracePoint
| ProgramSection::SocketFilter
| ProgramSection::Xdp {
@ -604,26 +604,44 @@ impl<'a> EbpfLoader<'a> {
data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level),
kind: ProbeKind::Return,
}),
ProgramSection::UProbe { sleepable } => {
ProgramSection::UProbe { sleepable, multi } => {
let mut data =
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
if *sleepable {
data.flags = BPF_F_SLEEPABLE;
}
if *multi {
data.expected_attach_type =
Some(bpf_attach_type::BPF_TRACE_UPROBE_MULTI);
}
Program::UProbe(UProbe {
data,
kind: ProbeKind::Entry,
compiled_for_multi: if *multi {
MultiCompileState::Multi
} else {
MultiCompileState::Single
},
})
}
ProgramSection::URetProbe { sleepable } => {
ProgramSection::URetProbe { sleepable, multi } => {
let mut data =
ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level);
if *sleepable {
data.flags = BPF_F_SLEEPABLE;
}
if *multi {
data.expected_attach_type =
Some(bpf_attach_type::BPF_TRACE_UPROBE_MULTI);
}
Program::UProbe(UProbe {
data,
kind: ProbeKind::Return,
compiled_for_multi: if *multi {
MultiCompileState::Multi
} else {
MultiCompileState::Single
},
})
}
ProgramSection::TracePoint => Program::TracePoint(TracePoint {

@ -1101,7 +1101,6 @@ macro_rules! impl_from_prog_info {
impl_from_prog_info!(
unsafe KProbe kind : ProbeKind,
unsafe UProbe kind : ProbeKind,
TracePoint,
Xdp attach_type : XdpAttachType,
SkMsg,

@ -1,29 +1,35 @@
//! User space probes.
use std::{
borrow::Cow,
collections::HashMap,
error::Error,
ffi::{CStr, OsStr, OsString},
ffi::{CStr, CString, OsStr, OsString},
fmt::{self, Write},
fs,
io::{self, BufRead as _, Cursor, Read as _},
mem,
os::{fd::AsFd as _, unix::ffi::OsStrExt as _},
os::{
fd::{AsFd as _, BorrowedFd},
unix::ffi::OsStrExt as _,
},
path::{Path, PathBuf},
sync::LazyLock,
};
use aya_obj::generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE};
use aya_obj::generated::{bpf_attach_type, bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE};
use libc::{ENOTSUP, EOPNOTSUPP};
use object::{Object as _, ObjectSection as _, ObjectSymbol as _, Symbol};
use thiserror::Error;
use crate::{
VerifierLogLevel,
programs::{
FdLink, LinkError, ProgramData, ProgramError, ProgramType, define_link_wrapper,
impl_try_into_fdlink, load_program,
FdLink, LinkError, ProgramData, ProgramError, ProgramFd, ProgramInfo, ProgramType,
define_link_wrapper, impl_try_into_fdlink, load_program,
perf_attach::{PerfLinkIdInner, PerfLinkInner},
probe::{OsStringExt as _, Probe, ProbeKind, attach},
},
sys::bpf_link_get_info_by_fd,
sys::{BpfLinkCreateArgs, LinkTarget, SyscallError, bpf_link_create, bpf_link_get_info_by_fd},
util::MMap,
};
@ -46,10 +52,19 @@ const LD_SO_CACHE_HEADER_NEW: &str = "glibc-ld.so.cache1.1";
pub struct UProbe {
pub(crate) data: ProgramData<UProbeLink>,
pub(crate) kind: ProbeKind,
pub(crate) compiled_for_multi: MultiCompileState,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum MultiCompileState {
Multi,
Single,
Unknown,
}
/// The location in the target object file to which the uprobe is to be
/// attached.
#[derive(Debug, Clone)]
pub enum UProbeAttachLocation<'a> {
/// The location of the target function in the target object file.
Symbol(&'a str),
@ -72,6 +87,28 @@ impl From<u64> for UProbeAttachLocation<'static> {
}
}
/// Options used when attaching a [`UProbe`] to multiple locations.
#[derive(Debug)]
pub struct UProbeMultiAttachOptions<'a> {
/// The target binary or shared object.
pub target: Cow<'a, Path>,
/// Restrict the attachment to a single process.
pub pid: Option<u32>,
/// Attach as [`ProbeKind::Entry`] or [`ProbeKind::Return`].
pub kind: ProbeKind,
/// Locations to attach to.
pub locations: Cow<'a, [UProbeMultiLocation<'a>]>,
}
/// Describes a single uprobe attachment within [`UProbeMultiAttachOptions`].
#[derive(Debug, Clone)]
pub struct UProbeMultiLocation<'a> {
/// The target location.
pub location: UProbeAttachLocation<'a>,
/// Optional per-location attach cookie.
pub cookie: Option<u64>,
}
impl UProbe {
/// The type of the program according to the kernel.
pub const PROGRAM_TYPE: ProgramType = ProgramType::KProbe;
@ -130,11 +167,84 @@ impl UProbe {
offset
};
let Self { data, kind } = self;
let Self { data, kind, .. } = self;
let path = path.as_os_str();
attach::<Self, _>(data, *kind, path, offset, pid, cookie)
}
/// Attaches the program to multiple locations in a single call.
///
/// This batches multiple uprobes for the same target binary or shared object into one
/// `bpf_link_create` invocation so a single eBPF program can watch many instruction offsets
/// without issuing one syscall per attach. The kernel must support `BPF_TRACE_UPROBE_MULTI`
/// links (introduced in Linux 6.6); otherwise this returns
/// [`UProbeError::UProbeMultiNotSupported`].
///
/// The eBPF program must be compiled with the `uprobe.multi/...` section (for example by using
/// `#[uprobe(multi)]` in Ayas macros) so that `BPF_PROG_LOAD` sets
/// `expected_attach_type = BPF_TRACE_UPROBE_MULTI`. Programs compiled without the multi section
/// prefix are rejected with [`UProbeError::ProgramNotMulti`].
///
/// # Parameters
///
/// The `opts` argument configures the attachment:
///
/// - [`UProbeMultiAttachOptions::target`] selects the binary or shared object path.
/// - [`UProbeMultiAttachOptions::pid`] optionally restricts events to a single process.
/// - [`UProbeMultiAttachOptions::kind`] chooses entry vs. return probes.
/// - [`UProbeMultiAttachOptions::locations`] describes every symbol/offset plus per-location
/// cookies; the list must be non-empty or the call fails with
/// [`UProbeError::InvalidLocations`].
pub fn attach_multi(
&mut self,
opts: UProbeMultiAttachOptions<'_>,
) -> Result<UProbeLinkId, ProgramError> {
match self.compiled_for_multi {
MultiCompileState::Multi => {}
MultiCompileState::Single => {
return Err(UProbeError::ProgramNotMulti.into());
}
MultiCompileState::Unknown => {
log::warn!("attaching multi-uprobe to a program without multi metadata");
}
}
let UProbeMultiAttachOptions {
target,
pid,
kind,
locations,
} = opts;
let proc_map = pid.map(ProcMap::new).transpose()?;
let resolved_path = resolve_attach_path(target.as_ref(), proc_map.as_ref())?;
if locations.is_empty() {
return Err(UProbeError::InvalidLocations {
path: resolved_path.to_path_buf(),
reason: String::from("no locations were provided"),
}
.into());
}
let path_buf = resolved_path.to_path_buf();
let (offsets, cookies) = resolve_multi_locations(&path_buf, locations)?;
let prog_fd = self.data.fd()?;
let prog_fd = prog_fd.as_fd();
let path_cstr = CString::new(path_buf.as_os_str().as_bytes()).map_err(|error| {
ProgramError::IOError(io::Error::new(io::ErrorKind::InvalidInput, error))
})?;
let link = try_attach_uprobe_multi_link(
prog_fd,
&path_cstr,
&offsets,
pid,
kind,
cookies.as_deref(),
)?;
self.data.links.insert(UProbeLink::from(link))
}
/// Creates a program from a pinned entry on a bpffs.
///
/// Existing links will not be populated. To work with existing links you should use [`crate::programs::links::PinnedLink`].
@ -143,7 +253,50 @@ impl UProbe {
/// the program being unloaded from the kernel if it is still pinned.
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 })
Ok(Self {
data,
kind,
compiled_for_multi: MultiCompileState::Unknown,
})
}
/// Constructs an instance of a [`Self`] from a [`ProgramInfo`].
///
/// This allows the caller to get a handle to an already loaded program from
/// the kernel without having to load it again.
///
/// # Errors
///
/// - If the program type reported by the kernel does not match
/// [`Self::PROGRAM_TYPE`].
/// - If the file descriptor of the program cannot be cloned.
///
/// # Safety
///
/// Caller must ensure the info actually describes a uprobe/uretprobe.
pub unsafe fn from_program_info(
info: ProgramInfo,
name: Cow<'static, str>,
kind: ProbeKind,
) -> Result<Self, ProgramError> {
if info.program_type() != Self::PROGRAM_TYPE.into() {
return Err(ProgramError::UnexpectedProgramType {});
}
// The generic `impl_from_prog_info!` macro can't populate `compiled_for_multi`,
// so we provide this bespoke implementation to keep the new state in sync.
let ProgramFd(fd) = info.fd()?;
let ProgramInfo(bpf_program_info) = info;
Ok(Self {
data: ProgramData::from_bpf_prog_info(
Some(name),
fd,
Path::new(""),
bpf_program_info,
VerifierLogLevel::default(),
)?,
kind,
compiled_for_multi: MultiCompileState::Unknown,
})
}
}
@ -196,6 +349,208 @@ where
})
}
fn resolve_multi_locations<'a>(
path: &Path,
locations: Cow<'a, [UProbeMultiLocation<'a>]>,
) -> Result<(Vec<u64>, Option<Vec<u64>>), UProbeError> {
let mut offsets = vec![0; locations.len()];
let mut cookies = Vec::with_capacity(locations.len());
let mut has_cookie = false;
let mut requests = Vec::new();
for (index, UProbeMultiLocation { location, cookie }) in locations.iter().enumerate() {
match location {
UProbeAttachLocation::Symbol(symbol) => requests.push(SymbolRequest {
index,
symbol,
additional: 0,
}),
UProbeAttachLocation::SymbolOffset(symbol, additional) => {
requests.push(SymbolRequest {
index,
symbol,
additional: *additional,
})
}
UProbeAttachLocation::AbsoluteOffset(offset) => {
offsets[index] = *offset;
}
}
has_cookie |= cookie.is_some();
cookies.push(cookie.unwrap_or(0));
}
if !requests.is_empty() {
// Every requested symbol must successfully resolve to a file offset, `resolve_symbols`
// propagates an error if any entry is missing so we never attach a partially resolved list.
let symbol_names: Vec<&str> = requests.iter().map(|req| req.symbol).collect();
let resolved = resolve_symbols(path, &symbol_names).map_err(|error| {
let symbol = match &error {
ResolveSymbolError::Unknown(symbol)
| ResolveSymbolError::NotInSection(symbol)
| ResolveSymbolError::SectionFileRangeNone(symbol, _)
| ResolveSymbolError::BuildIdMismatch(symbol) => symbol.clone(),
_ => symbol_names
.first()
.map(|symbol| (*symbol).to_string())
.unwrap_or_default(),
};
UProbeError::SymbolError {
symbol,
error: Box::new(error),
}
})?;
for (request, offset) in requests.iter().zip(resolved.into_iter()) {
offsets[request.index] = offset + request.additional;
}
}
let cookies = has_cookie.then_some(cookies);
Ok((offsets, cookies))
}
struct SymbolRequest<'a> {
index: usize,
symbol: &'a str,
additional: u64,
}
fn resolve_symbols(path: &Path, symbols: &[&str]) -> Result<Vec<u64>, ResolveSymbolError> {
if symbols.is_empty() {
return Ok(Vec::new());
}
let requests = build_symbol_requests(symbols);
let mut offsets = vec![None; symbols.len()];
let data = MMap::map_copy_read_only(path)?;
let obj = object::read::File::parse(data.as_ref())?;
resolve_symbols_in_object(&obj, &requests, &mut offsets)?;
if offsets.iter().all(Option::is_some) {
return finalize_symbol_offsets(offsets, symbols);
}
if let Some(symbol) = first_unresolved_symbol(symbols, &offsets) {
let debug_path = find_debug_path_in_object(&obj, path, symbol)?;
let data = MMap::map_copy_read_only(&debug_path)
.map_err(|e| ResolveSymbolError::DebuglinkAccessError(debug_path.clone(), e))?;
let debug_obj = object::read::File::parse(data.as_ref())?;
verify_build_ids(&obj, &debug_obj, symbol)?;
resolve_symbols_in_object(&debug_obj, &requests, &mut offsets)?;
}
finalize_symbol_offsets(offsets, symbols)
}
fn build_symbol_requests<'a>(symbols: &[&'a str]) -> HashMap<&'a str, Vec<usize>> {
let mut requests: HashMap<&'a str, Vec<usize>> = HashMap::new();
for (index, symbol) in symbols.iter().enumerate() {
requests.entry(*symbol).or_default().push(index);
}
requests
}
fn resolve_symbols_in_object(
obj: &object::File<'_>,
requests: &HashMap<&str, Vec<usize>>,
offsets: &mut [Option<u64>],
) -> Result<(), ResolveSymbolError> {
if requests.is_empty() {
return Ok(());
}
let mut remaining = requests
.values()
.flat_map(|indices| indices.iter())
.filter(|&&index| offsets[index].is_none())
.count();
for sym in obj.dynamic_symbols().chain(obj.symbols()) {
let symbol_name = match sym.name() {
Ok(name) => name,
Err(_) => continue,
};
let Some(indices) = requests.get(symbol_name) else {
continue;
};
let offset = symbol_translated_address(obj, sym, symbol_name)?;
for &index in indices {
if offsets[index].is_none() {
offsets[index] = Some(offset);
remaining -= 1;
if remaining == 0 {
return Ok(());
}
}
}
}
Ok(())
}
fn finalize_symbol_offsets(
offsets: Vec<Option<u64>>,
symbols: &[&str],
) -> Result<Vec<u64>, ResolveSymbolError> {
offsets
.into_iter()
.zip(symbols)
.map(|(offset, symbol)| {
offset.ok_or_else(|| ResolveSymbolError::Unknown((*symbol).to_string()))
})
.collect()
}
fn first_unresolved_symbol<'a>(symbols: &[&'a str], offsets: &[Option<u64>]) -> Option<&'a str> {
symbols
.iter()
.zip(offsets)
.find_map(|(symbol, offset)| offset.is_none().then_some(*symbol))
}
fn try_attach_uprobe_multi_link(
prog_fd: BorrowedFd<'_>,
path: &CString,
offsets: &[u64],
pid: Option<u32>,
kind: ProbeKind,
cookies: Option<&[u64]>,
) -> Result<PerfLinkInner, ProgramError> {
let flags = match kind {
ProbeKind::Entry => 0,
ProbeKind::Return => aya_obj::generated::BPF_F_UPROBE_MULTI_RETURN,
};
let args = BpfLinkCreateArgs::UProbeMulti {
path,
offsets,
ref_ctr_offsets: None,
cookies,
pid,
flags,
};
let link_fd = bpf_link_create(
prog_fd,
LinkTarget::None,
bpf_attach_type::BPF_TRACE_UPROBE_MULTI,
0,
Some(args),
)
.map_err(|io_error| match io_error.raw_os_error() {
Some(code) if code == ENOTSUP || code == EOPNOTSUPP => {
ProgramError::UProbeError(UProbeError::UProbeMultiNotSupported)
}
_ => ProgramError::SyscallError(SyscallError {
call: "bpf_link_create",
io_error,
}),
})?;
Ok(PerfLinkInner::Fd(FdLink::new(link_fd)))
}
// Only run this test on linux with glibc because only in that configuration do we know that we'll
// be dynamically linked to libc and can exercise resolving the path to libc via the current
// process's memory map.
@ -254,7 +609,9 @@ impl TryFrom<FdLink> for UProbeLink {
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_TRACING as u32) {
if info.type_ == (bpf_link_type::BPF_LINK_TYPE_TRACING as u32)
|| info.type_ == (bpf_link_type::BPF_LINK_TYPE_UPROBE_MULTI as u32)
{
return Ok(Self::new(PerfLinkInner::Fd(fd_link)));
}
Err(LinkError::InvalidLink)
@ -308,6 +665,23 @@ pub enum UProbeError {
#[source]
source: ProcMapError,
},
/// The kernel does not support multi-uprobe links.
#[error("uprobe_multi links are not supported by the running kernel")]
UProbeMultiNotSupported,
/// The provided locations were invalid.
#[error("invalid uprobe locations for `{path}`: {reason}")]
InvalidLocations {
/// path to target.
path: PathBuf,
/// human-readable reason.
reason: String,
},
/// The program was not compiled for multi-attach.
#[error("attach_multi requires a program compiled with #[uprobe(multi)]")]
ProgramNotMulti,
}
/// Error reading from /proc/pid/maps.
@ -690,32 +1064,11 @@ fn find_debug_path_in_object<'a>(
}
}
fn find_symbol_in_object<'a>(obj: &'a object::File<'a>, symbol: &str) -> Option<Symbol<'a, 'a>> {
obj.dynamic_symbols()
.chain(obj.symbols())
.find(|sym| sym.name().map(|name| name == symbol).unwrap_or(false))
}
fn resolve_symbol(path: &Path, symbol: &str) -> Result<u64, ResolveSymbolError> {
let data = MMap::map_copy_read_only(path)?;
let obj = object::read::File::parse(data.as_ref())?;
if let Some(sym) = find_symbol_in_object(&obj, symbol) {
symbol_translated_address(&obj, sym, symbol)
} else {
// Only search in the debug object if the symbol was not found in the main object
let debug_path = find_debug_path_in_object(&obj, path, symbol)?;
let debug_data = MMap::map_copy_read_only(&debug_path)
.map_err(|e| ResolveSymbolError::DebuglinkAccessError(debug_path, e))?;
let debug_obj = object::read::File::parse(debug_data.as_ref())?;
verify_build_ids(&obj, &debug_obj, symbol)?;
let sym = find_symbol_in_object(&debug_obj, symbol)
.ok_or_else(|| ResolveSymbolError::Unknown(symbol.to_string()))?;
symbol_translated_address(&debug_obj, sym, symbol)
}
let mut offsets = resolve_symbols(path, std::slice::from_ref(&symbol))?;
offsets
.pop()
.ok_or_else(|| ResolveSymbolError::Unknown(symbol.to_string()))
}
fn symbol_translated_address(

@ -384,15 +384,26 @@ pub(crate) enum LinkTarget<'f> {
Fd(BorrowedFd<'f>),
IfIndex(u32),
Iter,
None,
}
// Models https://github.com/torvalds/linux/blob/2144da25/include/uapi/linux/bpf.h#L1724-L1782.
pub(crate) enum BpfLinkCreateArgs<'a> {
TargetBtfId(u32),
// since kernel 5.15
PerfEvent { bpf_cookie: u64 },
PerfEvent {
bpf_cookie: u64,
},
// since kernel 6.6
Tcx(&'a LinkRef),
UProbeMulti {
path: &'a CStr,
offsets: &'a [u64],
ref_ctr_offsets: Option<&'a [u64]>,
cookies: Option<&'a [u64]>,
pid: Option<u32>,
flags: u32,
},
}
// since kernel 5.7
@ -418,7 +429,7 @@ pub(crate) fn bpf_link_create(
// fact, the kernel explicitly rejects non-zero target FDs for
// iterators:
// https://github.com/torvalds/linux/blob/v6.12/kernel/bpf/bpf_iter.c#L517-L518
LinkTarget::Iter => {}
LinkTarget::Iter | LinkTarget::None => {}
};
attr.link_create.attach_type = attach_type as u32;
attr.link_create.flags = flags;
@ -447,6 +458,27 @@ pub(crate) fn bpf_link_create(
.relative_id = id.to_owned();
}
},
BpfLinkCreateArgs::UProbeMulti {
path,
offsets,
ref_ctr_offsets,
cookies,
pid,
flags,
} => {
let multi = unsafe { &mut attr.link_create.__bindgen_anon_3.uprobe_multi };
multi.path = path.as_ptr() as u64;
multi.offsets = offsets.as_ptr() as u64;
multi.cnt = offsets.len() as u32;
multi.flags = flags;
multi.pid = pid.unwrap_or(0);
multi.ref_ctr_offsets = ref_ctr_offsets
.map(|slice| slice.as_ptr() as u64)
.unwrap_or_default();
multi.cookies = cookies
.map(|slice| slice.as_ptr() as u64)
.unwrap_or_default();
}
}
}

@ -104,6 +104,10 @@ path = "src/xdp_sec.rs"
name = "uprobe_cookie"
path = "src/uprobe_cookie.rs"
[[bin]]
name = "uprobe_multi"
path = "src/uprobe_multi.rs"
[[bin]]
name = "perf_event_bp"
path = "src/perf_event_bp.rs"

@ -0,0 +1,22 @@
#![no_std]
#![no_main]
#![expect(unused_crate_dependencies, reason = "used in other bins")]
use aya_ebpf::{
EbpfContext as _, helpers,
macros::{map, uprobe},
maps::RingBuf,
programs::ProbeContext,
};
#[cfg(not(test))]
extern crate ebpf_panic;
#[map]
static RING_BUF: RingBuf = RingBuf::with_byte_size(0, 0);
#[uprobe(multi)]
fn uprobe_multi(ctx: ProbeContext) {
let cookie = unsafe { helpers::bpf_get_attach_cookie(ctx.as_ptr()) };
let cookie_bytes = cookie.to_le_bytes();
let _ = RING_BUF.output::<[u8]>(cookie_bytes, 0);
}

@ -59,6 +59,7 @@ bpf_file!(
TWO_PROGS => "two_progs",
XDP_SEC => "xdp_sec",
UPROBE_COOKIE => "uprobe_cookie",
UPROBE_MULTI => "uprobe_multi",
);
#[cfg(test)]

@ -21,4 +21,5 @@ mod smoke;
mod strncmp;
mod tcx;
mod uprobe_cookie;
mod uprobe_multi;
mod xdp;

@ -0,0 +1,166 @@
use std::{borrow::Cow, path::Path};
use aya::{
EbpfLoader,
maps::ring_buf::RingBuf,
programs::{
ProbeKind, ProgramError, UProbe,
uprobe::{UProbeAttachLocation, UProbeMultiAttachOptions, UProbeMultiLocation},
},
util::KernelVersion,
};
const PROG_A: &str = "uprobe_multi_trigger_program_a";
const PROG_B: &str = "uprobe_multi_trigger_program_b";
const PROG_NO_COOKIE: &str = "uprobe_multi_trigger_program_no_cookie";
#[test_log::test]
fn test_uprobe_attach_multi() {
let kernel_version = KernelVersion::current().unwrap();
if kernel_version < KernelVersion::new(6, 6, 0) {
eprintln!(
"skipping test on kernel {kernel_version:?}, uprobe_multi links were added in 6.6"
);
return;
}
const RING_BUF_BYTE_SIZE: u32 = 512;
let mut bpf = EbpfLoader::new()
.map_max_entries("RING_BUF", RING_BUF_BYTE_SIZE)
.load(crate::UPROBE_MULTI)
.unwrap();
let ring_buf = match bpf.take_map("RING_BUF") {
Some(map) => map,
None => {
eprintln!("skipping test because RING_BUF map is unavailable");
return;
}
};
let mut ring_buf = RingBuf::try_from(ring_buf).unwrap();
let prog: &mut UProbe = bpf.program_mut("uprobe_multi").unwrap().try_into().unwrap();
prog.load().unwrap();
const COOKIE_A: u64 = 0x11;
const COOKIE_B: u64 = 0x22;
let locations = vec![
UProbeMultiLocation {
location: UProbeAttachLocation::Symbol(PROG_A),
cookie: Some(COOKIE_A),
},
UProbeMultiLocation {
location: UProbeAttachLocation::Symbol(PROG_B),
cookie: Some(COOKIE_B),
},
UProbeMultiLocation {
location: UProbeAttachLocation::Symbol(PROG_NO_COOKIE),
cookie: None,
},
];
let opts = UProbeMultiAttachOptions {
target: Cow::Borrowed(Path::new("/proc/self/exe")),
pid: None,
kind: ProbeKind::Entry,
locations: Cow::Owned(locations),
};
let link_id = match prog.attach_multi(opts) {
Ok(link) => link,
Err(aya::programs::ProgramError::UProbeError(
aya::programs::uprobe::UProbeError::UProbeMultiNotSupported,
)) => {
eprintln!("skipping test on kernel {kernel_version:?}, uprobe_multi not supported");
return;
}
Err(err) => panic!("attach_multi failed: {err:?}"),
};
// Drain any stale events emitted by other tests before we generate fresh ones.
while ring_buf.next().is_some() {}
uprobe_multi_trigger_program_a(1);
uprobe_multi_trigger_program_b(2);
uprobe_multi_trigger_program_no_cookie();
uprobe_multi_trigger_program_a(3);
prog.detach(link_id).unwrap();
// Fire another probe to ensure detach removed all active links.
uprobe_multi_trigger_program_a(3);
const EXP: &[u64] = &[COOKIE_A, COOKIE_B, 0, COOKIE_A];
let mut seen = Vec::new();
while let Some(record) = ring_buf.next() {
let data = record.as_ref();
match data.try_into() {
Ok(bytes) => seen.push(u64::from_le_bytes(bytes)),
Err(std::array::TryFromSliceError { .. }) => {
panic!("invalid ring buffer data: {data:x?}")
}
}
}
assert_eq!(seen, EXP);
}
#[test_log::test]
fn test_uprobe_attach_multi_rejects_non_multi_program() {
let kernel_version = KernelVersion::current().unwrap();
if kernel_version < KernelVersion::new(6, 6, 0) {
eprintln!(
"skipping test on kernel {kernel_version:?}, uprobe_multi links were added in 6.6"
);
return;
}
// Load the single-attach `uprobe_cookie` program to ensure `attach_multi`
// rejects plain uprobes.
let mut bpf = EbpfLoader::new()
.map_max_entries("RING_BUF", 64)
.load(crate::UPROBE_COOKIE)
.unwrap();
let prog: &mut UProbe = bpf
.program_mut("uprobe_cookie")
.unwrap()
.try_into()
.unwrap();
prog.load().unwrap();
let locations = vec![UProbeMultiLocation {
location: UProbeAttachLocation::Symbol(PROG_A),
cookie: None,
}];
let opts = UProbeMultiAttachOptions {
target: Cow::Borrowed(Path::new("/proc/self/exe")),
pid: None,
kind: ProbeKind::Entry,
locations: Cow::Owned(locations),
};
match prog.attach_multi(opts) {
Err(ProgramError::UProbeError(aya::programs::uprobe::UProbeError::ProgramNotMulti)) => {}
Err(ProgramError::UProbeError(
aya::programs::uprobe::UProbeError::UProbeMultiNotSupported,
)) => {
eprintln!("skipping test on kernel {kernel_version:?}, uprobe_multi not supported");
return;
}
Err(err) => panic!("attach_multi returned unexpected error: {err:?}"),
Ok(_) => panic!("attach_multi succeeded for non-multi program"),
}
}
#[unsafe(no_mangle)]
#[inline(never)]
extern "C" fn uprobe_multi_trigger_program_a(arg: u64) {
std::hint::black_box(arg);
}
#[unsafe(no_mangle)]
#[inline(never)]
extern "C" fn uprobe_multi_trigger_program_b(arg: u64) {
// Deliberately make the body differ from program_a so link-time ICF
// doesn't fold both symbols onto the same address.
std::hint::black_box(arg + 2);
}
#[unsafe(no_mangle)]
#[inline(never)]
extern "C" fn uprobe_multi_trigger_program_no_cookie() {
std::hint::black_box(());
}

@ -8647,8 +8647,10 @@ pub aya_obj::obj::ProgramSection::SockOps
pub aya_obj::obj::ProgramSection::SocketFilter
pub aya_obj::obj::ProgramSection::TracePoint
pub aya_obj::obj::ProgramSection::UProbe
pub aya_obj::obj::ProgramSection::UProbe::multi: bool
pub aya_obj::obj::ProgramSection::UProbe::sleepable: bool
pub aya_obj::obj::ProgramSection::URetProbe
pub aya_obj::obj::ProgramSection::URetProbe::multi: bool
pub aya_obj::obj::ProgramSection::URetProbe::sleepable: bool
pub aya_obj::obj::ProgramSection::Xdp
pub aya_obj::obj::ProgramSection::Xdp::attach_type: aya_obj::programs::xdp::XdpAttachType
@ -9510,8 +9512,10 @@ pub aya_obj::ProgramSection::SockOps
pub aya_obj::ProgramSection::SocketFilter
pub aya_obj::ProgramSection::TracePoint
pub aya_obj::ProgramSection::UProbe
pub aya_obj::ProgramSection::UProbe::multi: bool
pub aya_obj::ProgramSection::UProbe::sleepable: bool
pub aya_obj::ProgramSection::URetProbe
pub aya_obj::ProgramSection::URetProbe::multi: bool
pub aya_obj::ProgramSection::URetProbe::sleepable: bool
pub aya_obj::ProgramSection::Xdp
pub aya_obj::ProgramSection::Xdp::attach_type: aya_obj::programs::xdp::XdpAttachType

@ -7341,8 +7341,12 @@ pub aya::programs::uprobe::UProbeAttachLocation::Symbol(&'a str)
pub aya::programs::uprobe::UProbeAttachLocation::SymbolOffset(&'a str, u64)
impl core::convert::From<u64> for aya::programs::uprobe::UProbeAttachLocation<'static>
pub fn aya::programs::uprobe::UProbeAttachLocation<'static>::from(offset: u64) -> Self
impl<'a> core::clone::Clone for aya::programs::uprobe::UProbeAttachLocation<'a>
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::clone(&self) -> aya::programs::uprobe::UProbeAttachLocation<'a>
impl<'a> core::convert::From<&'a str> for aya::programs::uprobe::UProbeAttachLocation<'a>
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::from(s: &'a str) -> Self
impl<'a> core::fmt::Debug for aya::programs::uprobe::UProbeAttachLocation<'a>
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl<'a> core::marker::Freeze for aya::programs::uprobe::UProbeAttachLocation<'a>
impl<'a> core::marker::Send for aya::programs::uprobe::UProbeAttachLocation<'a>
impl<'a> core::marker::Sync for aya::programs::uprobe::UProbeAttachLocation<'a>
@ -7357,12 +7361,18 @@ pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::try_from(value: U) -> co
impl<T, U> core::convert::TryInto<U> for aya::programs::uprobe::UProbeAttachLocation<'a> where U: core::convert::TryFrom<T>
pub type aya::programs::uprobe::UProbeAttachLocation<'a>::Error = <U as core::convert::TryFrom<T>>::Error
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> alloc::borrow::ToOwned for aya::programs::uprobe::UProbeAttachLocation<'a> where T: core::clone::Clone
pub type aya::programs::uprobe::UProbeAttachLocation<'a>::Owned = T
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::clone_into(&self, target: &mut T)
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::to_owned(&self) -> T
impl<T> core::any::Any for aya::programs::uprobe::UProbeAttachLocation<'a> where T: 'static + ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for aya::programs::uprobe::UProbeAttachLocation<'a> where T: ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for aya::programs::uprobe::UProbeAttachLocation<'a> where T: ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::borrow_mut(&mut self) -> &mut T
impl<T> core::clone::CloneToUninit for aya::programs::uprobe::UProbeAttachLocation<'a> where T: core::clone::Clone
pub unsafe fn aya::programs::uprobe::UProbeAttachLocation<'a>::clone_to_uninit(&self, dest: *mut u8)
impl<T> core::convert::From<T> for aya::programs::uprobe::UProbeAttachLocation<'a>
pub fn aya::programs::uprobe::UProbeAttachLocation<'a>::from(t: T) -> T
pub enum aya::programs::uprobe::UProbeError
@ -7371,14 +7381,19 @@ pub aya::programs::uprobe::UProbeError::FileError::filename: std::path::PathBuf
pub aya::programs::uprobe::UProbeError::FileError::io_error: std::io::error::Error
pub aya::programs::uprobe::UProbeError::InvalidLdSoCache
pub aya::programs::uprobe::UProbeError::InvalidLdSoCache::io_error: &'static std::io::error::Error
pub aya::programs::uprobe::UProbeError::InvalidLocations
pub aya::programs::uprobe::UProbeError::InvalidLocations::path: std::path::PathBuf
pub aya::programs::uprobe::UProbeError::InvalidLocations::reason: alloc::string::String
pub aya::programs::uprobe::UProbeError::InvalidTarget
pub aya::programs::uprobe::UProbeError::InvalidTarget::path: std::path::PathBuf
pub aya::programs::uprobe::UProbeError::ProcMap
pub aya::programs::uprobe::UProbeError::ProcMap::pid: u32
pub aya::programs::uprobe::UProbeError::ProcMap::source: aya::programs::uprobe::ProcMapError
pub aya::programs::uprobe::UProbeError::ProgramNotMulti
pub aya::programs::uprobe::UProbeError::SymbolError
pub aya::programs::uprobe::UProbeError::SymbolError::error: alloc::boxed::Box<(dyn core::error::Error + core::marker::Send + core::marker::Sync)>
pub aya::programs::uprobe::UProbeError::SymbolError::symbol: alloc::string::String
pub aya::programs::uprobe::UProbeError::UProbeMultiNotSupported
impl core::convert::From<aya::programs::uprobe::UProbeError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::programs::uprobe::UProbeError) -> Self
impl core::error::Error for aya::programs::uprobe::UProbeError
@ -7415,7 +7430,9 @@ pub struct aya::programs::uprobe::UProbe
impl aya::programs::uprobe::UProbe
pub const aya::programs::uprobe::UProbe::PROGRAM_TYPE: aya::programs::ProgramType
pub fn aya::programs::uprobe::UProbe::attach<'loc, T: core::convert::AsRef<std::path::Path>, Loc: core::convert::Into<aya::programs::uprobe::UProbeAttachLocation<'loc>>>(&mut self, location: Loc, target: T, pid: core::option::Option<u32>, cookie: core::option::Option<u64>) -> core::result::Result<aya::programs::uprobe::UProbeLinkId, aya::programs::ProgramError>
pub fn aya::programs::uprobe::UProbe::attach_multi(&mut self, opts: aya::programs::uprobe::UProbeMultiAttachOptions<'_>) -> core::result::Result<aya::programs::uprobe::UProbeLinkId, aya::programs::ProgramError>
pub fn aya::programs::uprobe::UProbe::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P, kind: aya::programs::ProbeKind) -> core::result::Result<Self, aya::programs::ProgramError>
pub unsafe fn aya::programs::uprobe::UProbe::from_program_info(info: aya::programs::ProgramInfo, name: alloc::borrow::Cow<'static, str>, kind: aya::programs::ProbeKind) -> core::result::Result<Self, aya::programs::ProgramError>
pub fn aya::programs::uprobe::UProbe::kind(&self) -> aya::programs::ProbeKind
pub fn aya::programs::uprobe::UProbe::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
@ -7424,8 +7441,6 @@ pub fn aya::programs::uprobe::UProbe::take_link(&mut self, link_id: aya::program
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub unsafe fn aya::programs::uprobe::UProbe::from_program_info(info: aya::programs::ProgramInfo, name: alloc::borrow::Cow<'static, str>, kind: aya::programs::ProbeKind) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>
@ -7545,6 +7560,70 @@ impl<T> core::borrow::BorrowMut<T> for aya::programs::uprobe::UProbeLinkId where
pub fn aya::programs::uprobe::UProbeLinkId::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::programs::uprobe::UProbeLinkId
pub fn aya::programs::uprobe::UProbeLinkId::from(t: T) -> T
pub struct aya::programs::uprobe::UProbeMultiAttachOptions<'a>
pub aya::programs::uprobe::UProbeMultiAttachOptions::kind: aya::programs::ProbeKind
pub aya::programs::uprobe::UProbeMultiAttachOptions::locations: alloc::borrow::Cow<'a, [aya::programs::uprobe::UProbeMultiLocation<'a>]>
pub aya::programs::uprobe::UProbeMultiAttachOptions::pid: core::option::Option<u32>
pub aya::programs::uprobe::UProbeMultiAttachOptions::target: alloc::borrow::Cow<'a, std::path::Path>
impl<'a> core::fmt::Debug for aya::programs::uprobe::UProbeMultiAttachOptions<'a>
pub fn aya::programs::uprobe::UProbeMultiAttachOptions<'a>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl<'a> core::marker::Freeze for aya::programs::uprobe::UProbeMultiAttachOptions<'a>
impl<'a> core::marker::Send for aya::programs::uprobe::UProbeMultiAttachOptions<'a>
impl<'a> core::marker::Sync for aya::programs::uprobe::UProbeMultiAttachOptions<'a>
impl<'a> core::marker::Unpin for aya::programs::uprobe::UProbeMultiAttachOptions<'a>
impl<'a> core::panic::unwind_safe::RefUnwindSafe for aya::programs::uprobe::UProbeMultiAttachOptions<'a>
impl<'a> core::panic::unwind_safe::UnwindSafe for aya::programs::uprobe::UProbeMultiAttachOptions<'a>
impl<T, U> core::convert::Into<U> for aya::programs::uprobe::UProbeMultiAttachOptions<'a> where U: core::convert::From<T>
pub fn aya::programs::uprobe::UProbeMultiAttachOptions<'a>::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::uprobe::UProbeMultiAttachOptions<'a> where U: core::convert::Into<T>
pub type aya::programs::uprobe::UProbeMultiAttachOptions<'a>::Error = core::convert::Infallible
pub fn aya::programs::uprobe::UProbeMultiAttachOptions<'a>::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
impl<T, U> core::convert::TryInto<U> for aya::programs::uprobe::UProbeMultiAttachOptions<'a> where U: core::convert::TryFrom<T>
pub type aya::programs::uprobe::UProbeMultiAttachOptions<'a>::Error = <U as core::convert::TryFrom<T>>::Error
pub fn aya::programs::uprobe::UProbeMultiAttachOptions<'a>::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> core::any::Any for aya::programs::uprobe::UProbeMultiAttachOptions<'a> where T: 'static + ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeMultiAttachOptions<'a>::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for aya::programs::uprobe::UProbeMultiAttachOptions<'a> where T: ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeMultiAttachOptions<'a>::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for aya::programs::uprobe::UProbeMultiAttachOptions<'a> where T: ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeMultiAttachOptions<'a>::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::programs::uprobe::UProbeMultiAttachOptions<'a>
pub fn aya::programs::uprobe::UProbeMultiAttachOptions<'a>::from(t: T) -> T
pub struct aya::programs::uprobe::UProbeMultiLocation<'a>
pub aya::programs::uprobe::UProbeMultiLocation::cookie: core::option::Option<u64>
pub aya::programs::uprobe::UProbeMultiLocation::location: aya::programs::uprobe::UProbeAttachLocation<'a>
impl<'a> core::clone::Clone for aya::programs::uprobe::UProbeMultiLocation<'a>
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::clone(&self) -> aya::programs::uprobe::UProbeMultiLocation<'a>
impl<'a> core::fmt::Debug for aya::programs::uprobe::UProbeMultiLocation<'a>
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl<'a> core::marker::Freeze for aya::programs::uprobe::UProbeMultiLocation<'a>
impl<'a> core::marker::Send for aya::programs::uprobe::UProbeMultiLocation<'a>
impl<'a> core::marker::Sync for aya::programs::uprobe::UProbeMultiLocation<'a>
impl<'a> core::marker::Unpin for aya::programs::uprobe::UProbeMultiLocation<'a>
impl<'a> core::panic::unwind_safe::RefUnwindSafe for aya::programs::uprobe::UProbeMultiLocation<'a>
impl<'a> core::panic::unwind_safe::UnwindSafe for aya::programs::uprobe::UProbeMultiLocation<'a>
impl<T, U> core::convert::Into<U> for aya::programs::uprobe::UProbeMultiLocation<'a> where U: core::convert::From<T>
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::into(self) -> U
impl<T, U> core::convert::TryFrom<U> for aya::programs::uprobe::UProbeMultiLocation<'a> where U: core::convert::Into<T>
pub type aya::programs::uprobe::UProbeMultiLocation<'a>::Error = core::convert::Infallible
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::try_from(value: U) -> core::result::Result<T, <T as core::convert::TryFrom<U>>::Error>
impl<T, U> core::convert::TryInto<U> for aya::programs::uprobe::UProbeMultiLocation<'a> where U: core::convert::TryFrom<T>
pub type aya::programs::uprobe::UProbeMultiLocation<'a>::Error = <U as core::convert::TryFrom<T>>::Error
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::try_into(self) -> core::result::Result<U, <U as core::convert::TryFrom<T>>::Error>
impl<T> alloc::borrow::ToOwned for aya::programs::uprobe::UProbeMultiLocation<'a> where T: core::clone::Clone
pub type aya::programs::uprobe::UProbeMultiLocation<'a>::Owned = T
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::clone_into(&self, target: &mut T)
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::to_owned(&self) -> T
impl<T> core::any::Any for aya::programs::uprobe::UProbeMultiLocation<'a> where T: 'static + ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::type_id(&self) -> core::any::TypeId
impl<T> core::borrow::Borrow<T> for aya::programs::uprobe::UProbeMultiLocation<'a> where T: ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::borrow(&self) -> &T
impl<T> core::borrow::BorrowMut<T> for aya::programs::uprobe::UProbeMultiLocation<'a> where T: ?core::marker::Sized
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::borrow_mut(&mut self) -> &mut T
impl<T> core::clone::CloneToUninit for aya::programs::uprobe::UProbeMultiLocation<'a> where T: core::clone::Clone
pub unsafe fn aya::programs::uprobe::UProbeMultiLocation<'a>::clone_to_uninit(&self, dest: *mut u8)
impl<T> core::convert::From<T> for aya::programs::uprobe::UProbeMultiLocation<'a>
pub fn aya::programs::uprobe::UProbeMultiLocation<'a>::from(t: T) -> T
pub mod aya::programs::xdp
pub enum aya::programs::xdp::XdpError
pub aya::programs::xdp::XdpError::NetlinkError(aya::sys::netlink::NetlinkError)
@ -8622,14 +8701,19 @@ pub aya::programs::UProbeError::FileError::filename: std::path::PathBuf
pub aya::programs::UProbeError::FileError::io_error: std::io::error::Error
pub aya::programs::UProbeError::InvalidLdSoCache
pub aya::programs::UProbeError::InvalidLdSoCache::io_error: &'static std::io::error::Error
pub aya::programs::UProbeError::InvalidLocations
pub aya::programs::UProbeError::InvalidLocations::path: std::path::PathBuf
pub aya::programs::UProbeError::InvalidLocations::reason: alloc::string::String
pub aya::programs::UProbeError::InvalidTarget
pub aya::programs::UProbeError::InvalidTarget::path: std::path::PathBuf
pub aya::programs::UProbeError::ProcMap
pub aya::programs::UProbeError::ProcMap::pid: u32
pub aya::programs::UProbeError::ProcMap::source: aya::programs::uprobe::ProcMapError
pub aya::programs::UProbeError::ProgramNotMulti
pub aya::programs::UProbeError::SymbolError
pub aya::programs::UProbeError::SymbolError::error: alloc::boxed::Box<(dyn core::error::Error + core::marker::Send + core::marker::Sync)>
pub aya::programs::UProbeError::SymbolError::symbol: alloc::string::String
pub aya::programs::UProbeError::UProbeMultiNotSupported
impl core::convert::From<aya::programs::uprobe::UProbeError> for aya::programs::ProgramError
pub fn aya::programs::ProgramError::from(source: aya::programs::uprobe::UProbeError) -> Self
impl core::error::Error for aya::programs::uprobe::UProbeError
@ -10148,7 +10232,9 @@ pub struct aya::programs::UProbe
impl aya::programs::uprobe::UProbe
pub const aya::programs::uprobe::UProbe::PROGRAM_TYPE: aya::programs::ProgramType
pub fn aya::programs::uprobe::UProbe::attach<'loc, T: core::convert::AsRef<std::path::Path>, Loc: core::convert::Into<aya::programs::uprobe::UProbeAttachLocation<'loc>>>(&mut self, location: Loc, target: T, pid: core::option::Option<u32>, cookie: core::option::Option<u64>) -> core::result::Result<aya::programs::uprobe::UProbeLinkId, aya::programs::ProgramError>
pub fn aya::programs::uprobe::UProbe::attach_multi(&mut self, opts: aya::programs::uprobe::UProbeMultiAttachOptions<'_>) -> core::result::Result<aya::programs::uprobe::UProbeLinkId, aya::programs::ProgramError>
pub fn aya::programs::uprobe::UProbe::from_pin<P: core::convert::AsRef<std::path::Path>>(path: P, kind: aya::programs::ProbeKind) -> core::result::Result<Self, aya::programs::ProgramError>
pub unsafe fn aya::programs::uprobe::UProbe::from_program_info(info: aya::programs::ProgramInfo, name: alloc::borrow::Cow<'static, str>, kind: aya::programs::ProbeKind) -> core::result::Result<Self, aya::programs::ProgramError>
pub fn aya::programs::uprobe::UProbe::kind(&self) -> aya::programs::ProbeKind
pub fn aya::programs::uprobe::UProbe::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
@ -10157,8 +10243,6 @@ pub fn aya::programs::uprobe::UProbe::take_link(&mut self, link_id: aya::program
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub unsafe fn aya::programs::uprobe::UProbe::from_program_info(info: aya::programs::ProgramInfo, name: alloc::borrow::Cow<'static, str>, kind: aya::programs::ProbeKind) -> core::result::Result<Self, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::info(&self) -> core::result::Result<aya::programs::ProgramInfo, aya::programs::ProgramError>
impl aya::programs::uprobe::UProbe
pub fn aya::programs::uprobe::UProbe::pin<P: core::convert::AsRef<std::path::Path>>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError>

Loading…
Cancel
Save