diff --git a/aya-ebpf-macros/src/hid_bpf.rs b/aya-ebpf-macros/src/hid_bpf.rs new file mode 100644 index 000000000..b7d6f0da8 --- /dev/null +++ b/aya-ebpf-macros/src/hid_bpf.rs @@ -0,0 +1,131 @@ +use std::borrow::Cow; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ItemFn, Result}; + +use crate::args::err_on_unknown_args; + +/// The type of HID-BPF callback. +#[derive(Debug, Clone, Copy)] +pub(crate) enum HidBpfKind { + /// Called for each HID input report. + DeviceEvent, + /// Called to fix report descriptor at probe time. + RdescFixup, + /// Called for hardware requests (feature reports, etc). + HwRequest, + /// Called for output reports. + HwOutputReport, +} + +impl HidBpfKind { + fn section_name(&self) -> &'static str { + match self { + Self::DeviceEvent => "struct_ops/hid_device_event", + Self::RdescFixup => "struct_ops/hid_rdesc_fixup", + Self::HwRequest => "struct_ops/hid_hw_request", + Self::HwOutputReport => "struct_ops/hid_hw_output_report", + } + } +} + +pub(crate) struct HidBpf { + kind: HidBpfKind, + item: ItemFn, +} + +impl HidBpf { + pub(crate) fn parse(kind: HidBpfKind, attrs: TokenStream, item: TokenStream) -> Result { + let item = syn::parse2(item)?; + let args = syn::parse2(attrs)?; + err_on_unknown_args(&args)?; + Ok(Self { kind, item }) + } + + pub(crate) fn expand(&self) -> TokenStream { + let Self { kind, item } = self; + let ItemFn { + attrs: _, + vis, + sig, + block: _, + } = item; + let fn_name = &sig.ident; + let section_name: Cow<'_, _> = kind.section_name().into(); + + // IMPORTANT: The entry point MUST use *mut hid_bpf_ctx (not c_void) + // to generate correct BTF for kfuncs like hid_bpf_get_data. + // The kernel verifier checks that kfunc args match BTF struct types. + quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = #section_name)] + #vis fn #fn_name(ctx: *mut ::aya_ebpf::programs::hid_bpf::hid_bpf_ctx) -> i32 { + return #fn_name(unsafe { ::aya_ebpf::programs::HidBpfContext::new(ctx) }); + + #item + } + } + } +} + +#[cfg(test)] +mod tests { + use syn::parse_quote; + + use super::*; + + #[test] + fn test_hid_device_event() { + let prog = HidBpf::parse( + HidBpfKind::DeviceEvent, + parse_quote! {}, + parse_quote! { + fn device_event(ctx: HidBpfContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand(); + let expected = quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = "struct_ops/hid_device_event")] + fn device_event(ctx: *mut ::aya_ebpf::programs::hid_bpf::hid_bpf_ctx) -> i32 { + return device_event(unsafe { ::aya_ebpf::programs::HidBpfContext::new(ctx) }); + + fn device_event(ctx: HidBpfContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } + + #[test] + fn test_hid_rdesc_fixup() { + let prog = HidBpf::parse( + HidBpfKind::RdescFixup, + parse_quote! {}, + parse_quote! { + fn rdesc_fixup(ctx: HidBpfContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand(); + let expected = quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = "struct_ops/hid_rdesc_fixup")] + fn rdesc_fixup(ctx: *mut ::aya_ebpf::programs::hid_bpf::hid_bpf_ctx) -> i32 { + return rdesc_fixup(unsafe { ::aya_ebpf::programs::HidBpfContext::new(ctx) }); + + fn rdesc_fixup(ctx: HidBpfContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } +} diff --git a/aya-ebpf-macros/src/lib.rs b/aya-ebpf-macros/src/lib.rs index 5c9878b0e..8c24baa03 100644 --- a/aya-ebpf-macros/src/lib.rs +++ b/aya-ebpf-macros/src/lib.rs @@ -12,6 +12,7 @@ mod cgroup_sysctl; mod fentry; mod fexit; mod flow_dissector; +mod hid_bpf; mod kprobe; mod lsm; mod lsm_cgroup; @@ -23,6 +24,7 @@ mod sk_msg; mod sk_skb; mod sock_ops; mod socket_filter; +mod struct_ops; mod tc; mod tracepoint; mod uprobe; @@ -39,6 +41,7 @@ use cgroup_sysctl::CgroupSysctl; use fentry::FEntry; use fexit::FExit; use flow_dissector::FlowDissector; +use hid_bpf::{HidBpf, HidBpfKind}; use kprobe::{KProbe, KProbeKind}; use lsm::Lsm; use lsm_cgroup::LsmCgroup; @@ -51,6 +54,7 @@ use sk_msg::SkMsg; use sk_skb::{SkSkb, SkSkbKind}; use sock_ops::SockOps; use socket_filter::SocketFilter; +use struct_ops::StructOps; use tc::SchedClassifier; use tracepoint::TracePoint; use uprobe::{UProbe, UProbeKind}; @@ -601,6 +605,154 @@ pub fn fexit(attrs: TokenStream, item: TokenStream) -> TokenStream { .into() } +/// Marks a function as an eBPF struct_ops program. +/// +/// Struct ops programs implement kernel interfaces like `hid_bpf_ops` for +/// HID device handling or `sched_ext_ops` for custom schedulers. +/// +/// # Arguments +/// +/// * `name` - Optional name for the struct_ops callback. If not specified, +/// the function name is used. +/// * `sleepable` - Mark the program as sleepable. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.6. +/// +/// # Example +/// +/// ```no_run +/// use aya_ebpf::{macros::struct_ops, programs::StructOpsContext}; +/// +/// #[struct_ops] +/// fn my_callback(ctx: StructOpsContext) -> i32 { +/// 0 +/// } +/// ``` +#[proc_macro_attribute] +pub fn struct_ops(attrs: TokenStream, item: TokenStream) -> TokenStream { + match StructOps::parse(attrs.into(), item.into()) { + Ok(prog) => prog.expand(), + Err(err) => err.into_compile_error(), + } + .into() +} + +/// Marks a function as an HID-BPF device event handler. +/// +/// This callback is invoked for each HID input report received from the device. +/// The function should return 0 to pass the event through, or modify the report +/// data and return a new size. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 6.3. +/// +/// # Example +/// +/// ```no_run +/// use aya_ebpf::{macros::hid_device_event, programs::HidBpfContext}; +/// +/// #[hid_device_event] +/// fn device_event(ctx: HidBpfContext) -> i32 { +/// 0 +/// } +/// ``` +#[proc_macro_attribute] +pub fn hid_device_event(attrs: TokenStream, item: TokenStream) -> TokenStream { + match HidBpf::parse(HidBpfKind::DeviceEvent, attrs.into(), item.into()) { + Ok(prog) => prog.expand(), + Err(err) => err.into_compile_error(), + } + .into() +} + +/// Marks a function as an HID-BPF report descriptor fixup handler. +/// +/// This callback is invoked once at device probe time to allow modifying the +/// HID report descriptor. The function should return 0 to keep the original +/// descriptor, or return a positive value indicating the new descriptor size. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 6.3. +/// +/// # Example +/// +/// ```no_run +/// use aya_ebpf::{macros::hid_rdesc_fixup, programs::HidBpfContext}; +/// +/// #[hid_rdesc_fixup] +/// fn rdesc_fixup(ctx: HidBpfContext) -> i32 { +/// 0 +/// } +/// ``` +#[proc_macro_attribute] +pub fn hid_rdesc_fixup(attrs: TokenStream, item: TokenStream) -> TokenStream { + match HidBpf::parse(HidBpfKind::RdescFixup, attrs.into(), item.into()) { + Ok(prog) => prog.expand(), + Err(err) => err.into_compile_error(), + } + .into() +} + +/// Marks a function as an HID-BPF hardware request handler. +/// +/// This callback is invoked when the kernel sends hardware requests like +/// GET_REPORT or SET_REPORT. Useful for intercepting and modifying feature +/// reports. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 6.3. +/// +/// # Example +/// +/// ```no_run +/// use aya_ebpf::{macros::hid_hw_request, programs::HidBpfContext}; +/// +/// #[hid_hw_request] +/// fn hw_request(ctx: HidBpfContext) -> i32 { +/// 0 +/// } +/// ``` +#[proc_macro_attribute] +pub fn hid_hw_request(attrs: TokenStream, item: TokenStream) -> TokenStream { + match HidBpf::parse(HidBpfKind::HwRequest, attrs.into(), item.into()) { + Ok(prog) => prog.expand(), + Err(err) => err.into_compile_error(), + } + .into() +} + +/// Marks a function as an HID-BPF output report handler. +/// +/// This callback is invoked when an output report is sent to the device. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 6.3. +/// +/// # Example +/// +/// ```no_run +/// use aya_ebpf::{macros::hid_hw_output_report, programs::HidBpfContext}; +/// +/// #[hid_hw_output_report] +/// fn hw_output_report(ctx: HidBpfContext) -> i32 { +/// 0 +/// } +/// ``` +#[proc_macro_attribute] +pub fn hid_hw_output_report(attrs: TokenStream, item: TokenStream) -> TokenStream { + match HidBpf::parse(HidBpfKind::HwOutputReport, attrs.into(), item.into()) { + Ok(prog) => prog.expand(), + Err(err) => err.into_compile_error(), + } + .into() +} + /// Marks a function as an eBPF Flow Dissector program. /// /// Flow dissector is a program type that parses metadata out of the packets. diff --git a/aya-ebpf-macros/src/struct_ops.rs b/aya-ebpf-macros/src/struct_ops.rs new file mode 100644 index 000000000..02ae6acdb --- /dev/null +++ b/aya-ebpf-macros/src/struct_ops.rs @@ -0,0 +1,151 @@ +use std::borrow::Cow; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ItemFn, Result}; + +use crate::args::{err_on_unknown_args, pop_bool_arg, pop_string_arg}; + +pub(crate) struct StructOps { + item: ItemFn, + name: Option, + sleepable: bool, +} + +impl StructOps { + pub(crate) fn parse(attrs: TokenStream, item: TokenStream) -> Result { + let item = syn::parse2(item)?; + let mut args = syn::parse2(attrs)?; + let name = pop_string_arg(&mut args, "name"); + let sleepable = pop_bool_arg(&mut args, "sleepable"); + err_on_unknown_args(&args)?; + Ok(Self { + item, + name, + sleepable, + }) + } + + pub(crate) fn expand(&self) -> TokenStream { + let Self { + item, + name, + sleepable, + } = self; + let ItemFn { + attrs: _, + vis, + sig, + block: _, + } = item; + let section_prefix = if *sleepable { + "struct_ops.s" + } else { + "struct_ops" + }; + let fn_name = &sig.ident; + let section_name: Cow<'_, _> = if let Some(name) = name { + format!("{section_prefix}/{name}").into() + } else { + format!("{section_prefix}/{fn_name}").into() + }; + quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = #section_name)] + #vis fn #fn_name(ctx: *mut ::core::ffi::c_void) -> i32 { + return #fn_name(::aya_ebpf::programs::StructOpsContext::new(ctx)); + + #item + } + } + } +} + +#[cfg(test)] +mod tests { + use syn::parse_quote; + + use super::*; + + #[test] + fn test_struct_ops() { + let prog = StructOps::parse( + parse_quote! {}, + parse_quote! { + fn my_callback(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand(); + let expected = quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = "struct_ops/my_callback")] + fn my_callback(ctx: *mut ::core::ffi::c_void) -> i32 { + return my_callback(::aya_ebpf::programs::StructOpsContext::new(ctx)); + + fn my_callback(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } + + #[test] + fn test_struct_ops_with_name() { + let prog = StructOps::parse( + parse_quote! { + name = "hid_device_event" + }, + parse_quote! { + fn my_handler(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand(); + let expected = quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = "struct_ops/hid_device_event")] + fn my_handler(ctx: *mut ::core::ffi::c_void) -> i32 { + return my_handler(::aya_ebpf::programs::StructOpsContext::new(ctx)); + + fn my_handler(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } + + #[test] + fn test_struct_ops_sleepable() { + let prog = StructOps::parse( + parse_quote! { + sleepable + }, + parse_quote! { + fn my_callback(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + }, + ) + .unwrap(); + let expanded = prog.expand(); + let expected = quote! { + #[unsafe(no_mangle)] + #[unsafe(link_section = "struct_ops.s/my_callback")] + fn my_callback(ctx: *mut ::core::ffi::c_void) -> i32 { + return my_callback(::aya_ebpf::programs::StructOpsContext::new(ctx)); + + fn my_callback(ctx: &mut aya_ebpf::programs::StructOpsContext) -> i32 { + 0 + } + } + }; + assert_eq!(expected.to_string(), expanded.to_string()); + } +} diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index ff28b1648..c053c91b7 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -34,6 +34,10 @@ pub(crate) const MAX_SPEC_LEN: usize = 64; /// The error type returned when `BTF` operations fail. #[derive(thiserror::Error, Debug)] pub enum BtfError { + /// BTF is not available + #[error("BTF is not available")] + NoBtf, + #[cfg(feature = "std")] /// Error parsing file #[error("error parsing {path}")] @@ -448,7 +452,67 @@ impl Btf { }) } - pub(crate) fn type_size(&self, root_type_id: u32) -> Result { + /// Returns the index of a struct member by name. + /// + /// This is useful for struct_ops programs where `expected_attach_type` needs + /// to be set to the member index in the kernel struct. + pub fn struct_member_index( + &self, + struct_type_id: u32, + member_name: &str, + ) -> Result { + let ty = self.types.type_by_id(struct_type_id)?; + let members = match ty { + BtfType::Struct(s) => &s.members, + _ => { + return Err(BtfError::UnexpectedBtfType { + type_id: struct_type_id, + }); + } + }; + for (index, member) in members.iter().enumerate() { + let name = self.string_at(member.name_offset)?; + if name == member_name { + return Ok(index as u32); + } + } + Err(BtfError::UnknownBtfTypeName { + type_name: member_name.to_owned(), + }) + } + + /// Returns the byte offset of a struct member by name. + /// + /// This is useful for struct_ops where we need to find the offset of the + /// `data` field within the kernel wrapper struct (e.g., `bpf_struct_ops_hid_bpf_ops`). + pub fn struct_member_byte_offset( + &self, + struct_type_id: u32, + member_name: &str, + ) -> Result { + let ty = self.types.type_by_id(struct_type_id)?; + let members = match ty { + BtfType::Struct(s) => &s.members, + _ => { + return Err(BtfError::UnexpectedBtfType { + type_id: struct_type_id, + }); + } + }; + for member in members.iter() { + let name = self.string_at(member.name_offset)?; + if name == member_name { + // offset is in bits, convert to bytes + return Ok(member.offset / 8); + } + } + Err(BtfError::UnknownBtfTypeName { + type_name: member_name.to_owned(), + }) + } + + /// Returns the size of a BTF type in bytes. + pub fn type_size(&self, root_type_id: u32) -> Result { let mut type_id = root_type_id; let mut n_elems = 1; for () in core::iter::repeat_n((), MAX_RESOLVE_DEPTH) { @@ -569,8 +633,23 @@ impl Btf { d.name_offset = self.add_string(&fixed_name); } - // There are some cases when the compiler does indeed populate the size. - if d.size > 0 { + // .ksyms is a pseudo-section for extern kernel symbols (see EbpfSectionKind::Ksyms). + // It has no real ELF section and the kernel rejects DATASEC with + // size=0 and non-empty entries. Replace with INT to remove it. + const KSYMS_SECTION: &str = ".ksyms"; + if name == KSYMS_SECTION { + debug!("{kind} {name}: pseudo-section, replacing with INT"); + let old_size = d.type_info_size(); + let new_type = + BtfType::Int(Int::new(d.name_offset, 0, IntEncoding::None, 0)); + let new_size = new_type.type_info_size(); + // Update header to reflect the size change + self.header.type_len = + self.header.type_len - old_size as u32 + new_size as u32; + self.header.str_off = self.header.type_len; + *t = new_type; + } else if d.size > 0 { + // There are some cases when the compiler does indeed populate the size. debug!("{kind} {name}: size fixup not required"); } else { // We need to get the size of the section from the ELF file. @@ -700,6 +779,13 @@ impl Btf { if !features.btf_func { debug!("{kind}: not supported. replacing with TYPEDEF"); *t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type)); + } else if ty.linkage() == FuncLinkage::Extern { + // BTF_FUNC_EXTERN is used for kfuncs (kernel functions). + // These are resolved from kernel BTF at load time, so we + // should not include them in the program BTF. Replace with + // TYPEDEF to preserve type info without the problematic linkage. + debug!("{kind} {name}: extern kfunc, replacing with TYPEDEF"); + *t = BtfType::Typedef(Typedef::new(ty.name_offset, ty.btf_type)); } else if !features.btf_func_global || name == "memset" || name == "memcpy" diff --git a/aya-obj/src/lib.rs b/aya-obj/src/lib.rs index 6a6f44794..7fc39e4e4 100644 --- a/aya-obj/src/lib.rs +++ b/aya-obj/src/lib.rs @@ -41,7 +41,7 @@ //! let text_sections = std::collections::HashSet::new(); //! #[cfg(not(feature = "std"))] //! let text_sections = hashbrown::HashSet::new(); -//! object.relocate_calls(&text_sections).unwrap(); +//! object.relocate_calls(&text_sections, None).unwrap(); //! object.relocate_maps(std::iter::empty(), &text_sections).unwrap(); //! //! // Run with rbpf diff --git a/aya-obj/src/maps.rs b/aya-obj/src/maps.rs index 217b401b5..0ef3e3023 100644 --- a/aya-obj/src/maps.rs +++ b/aya-obj/src/maps.rs @@ -3,7 +3,10 @@ use alloc::vec::Vec; use core::mem; -use crate::{EbpfSectionKind, InvalidTypeBinding}; +use crate::{ + EbpfSectionKind, InvalidTypeBinding, + generated::{bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS, BPF_F_LINK}, +}; impl TryFrom for crate::generated::bpf_map_type { type Error = InvalidTypeBinding; @@ -138,6 +141,8 @@ pub enum Map { Legacy(LegacyMap), /// A map defined in the `.maps` section Btf(BtfMap), + /// A struct_ops map defined in `.struct_ops` or `.struct_ops.link` sections + StructOps(StructOpsMap), } impl Map { @@ -146,6 +151,7 @@ impl Map { match self { Self::Legacy(m) => m.def.map_type, Self::Btf(m) => m.def.map_type, + Self::StructOps(_) => BPF_MAP_TYPE_STRUCT_OPS as u32, } } @@ -154,6 +160,8 @@ impl Map { match self { Self::Legacy(m) => m.def.key_size, Self::Btf(m) => m.def.key_size, + // struct_ops maps always have key_size of 4 + Self::StructOps(_) => 4, } } @@ -162,6 +170,8 @@ impl Map { match self { Self::Legacy(m) => m.def.value_size, Self::Btf(m) => m.def.value_size, + // For struct_ops, value_size is the size of the struct data + Self::StructOps(m) => m.data.len() as u32, } } @@ -170,6 +180,8 @@ impl Map { match self { Self::Legacy(m) => m.def.value_size = size, Self::Btf(m) => m.def.value_size = size, + // struct_ops value_size is determined by the data, cannot be set + Self::StructOps(_) => {} } } @@ -178,6 +190,8 @@ impl Map { match self { Self::Legacy(m) => m.def.max_entries, Self::Btf(m) => m.def.max_entries, + // struct_ops maps always have max_entries of 1 + Self::StructOps(_) => 1, } } @@ -186,6 +200,8 @@ impl Map { match self { Self::Legacy(m) => m.def.max_entries = v, Self::Btf(m) => m.def.max_entries = v, + // struct_ops max_entries is always 1, cannot be changed + Self::StructOps(_) => {} } } @@ -194,6 +210,13 @@ impl Map { match self { Self::Legacy(m) => m.def.map_flags, Self::Btf(m) => m.def.map_flags, + Self::StructOps(m) => { + if m.is_link { + BPF_F_LINK + } else { + 0 + } + } } } @@ -202,6 +225,7 @@ impl Map { match self { Self::Legacy(m) => m.def.pinning, Self::Btf(m) => m.def.pinning, + Self::StructOps(_) => PinningType::None, } } @@ -210,6 +234,7 @@ impl Map { match self { Self::Legacy(m) => &m.data, Self::Btf(m) => &m.data, + Self::StructOps(m) => &m.data, } } @@ -218,6 +243,7 @@ impl Map { match self { Self::Legacy(m) => m.data.as_mut(), Self::Btf(m) => m.data.as_mut(), + Self::StructOps(m) => m.data.as_mut(), } } @@ -226,6 +252,7 @@ impl Map { match self { Self::Legacy(m) => m.section_index, Self::Btf(m) => m.section_index, + Self::StructOps(m) => m.section_index, } } @@ -234,6 +261,13 @@ impl Map { match self { Self::Legacy(m) => m.section_kind, Self::Btf(_) => EbpfSectionKind::BtfMaps, + Self::StructOps(m) => { + if m.is_link { + EbpfSectionKind::StructOpsLink + } else { + EbpfSectionKind::StructOps + } + } } } @@ -245,6 +279,18 @@ impl Map { match self { Self::Legacy(m) => m.symbol_index, Self::Btf(m) => Some(m.symbol_index), + Self::StructOps(m) => Some(m.symbol_index), + } + } + + /// Returns the BTF value type ID for struct_ops maps. + /// + /// This is the type ID of the struct_ops type in the program's BTF. + pub fn btf_value_type_id(&self) -> Option { + match self { + Self::Legacy(_) => None, + Self::Btf(m) => Some(m.def.btf_value_type_id), + Self::StructOps(m) => Some(m.btf_type_id), } } } @@ -280,3 +326,40 @@ pub struct BtfMap { pub(crate) symbol_index: usize, pub(crate) data: Vec, } + +/// Information about a function pointer field in a struct_ops struct. +#[derive(Debug, Clone)] +pub struct StructOpsFuncInfo { + /// The name of the struct member (function pointer field) + pub member_name: alloc::string::String, + /// The offset of this member within the struct (in bytes) + pub member_offset: u32, + /// The name of the BPF program that implements this callback + pub prog_name: alloc::string::String, +} + +/// A struct_ops map, defined in `.struct_ops` or `.struct_ops.link` sections. +/// +/// Struct_ops maps are special BPF maps that allow implementing kernel subsystem +/// callbacks (like tcp_congestion_ops, hid_bpf_ops, sched_ext_ops) in BPF programs. +#[derive(Debug, Clone)] +pub struct StructOpsMap { + /// The name of the struct_ops type (e.g., "hid_bpf_ops", "tcp_congestion_ops") + pub type_name: alloc::string::String, + /// BTF type ID of the struct_ops type + pub btf_type_id: u32, + /// The section index where this struct_ops was defined + pub section_index: usize, + /// The symbol index for this struct_ops variable + pub symbol_index: usize, + /// The struct instance data (will have program FDs filled in during loading) + pub data: Vec, + /// Whether this is a link-based struct_ops (from `.struct_ops.link` section) + pub is_link: bool, + /// Information about function pointer fields that need BPF programs + pub func_info: Vec, + /// Byte offset of the `data` field within the kernel wrapper struct + /// (e.g., offset of `data` in `bpf_struct_ops_hid_bpf_ops`) + /// This offset must be added to all member offsets when writing to the struct. + pub data_offset: u32, +} diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index c3d0d107f..4b065edcc 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -217,7 +217,6 @@ pub struct Function { /// - `action` /// - `sk_reuseport/migrate`, `sk_reuseport` /// - `syscall` -/// - `struct_ops+` /// - `fmod_ret+`, `fmod_ret.s+` /// - `iter+`, `iter.s+` #[derive(Debug, Clone)] @@ -276,6 +275,13 @@ pub enum ProgramSection { Iter { sleepable: bool, }, + StructOps { + sleepable: bool, + /// The struct member name this program implements (from section name) + member_name: String, + }, + /// BPF_PROG_TYPE_SYSCALL - invoked via bpf() syscall + Syscall, } impl FromStr for ProgramSection { @@ -429,6 +435,15 @@ impl FromStr for ProgramSection { "sk_lookup" => Self::SkLookup, "iter" => Self::Iter { sleepable: false }, "iter.s" => Self::Iter { sleepable: true }, + "struct_ops" => Self::StructOps { + sleepable: false, + member_name: next()?.to_string(), + }, + "struct_ops.s" => Self::StructOps { + sleepable: true, + member_name: next()?.to_string(), + }, + "syscall" => Self::Syscall, _ => { return Err(ParseError::InvalidProgramSection { section: section.to_owned(), @@ -831,6 +846,120 @@ impl Object { Ok(()) } + fn parse_struct_ops_section(&mut self, section: &Section<'_>) -> Result<(), ParseError> { + let btf = self.btf.as_ref().ok_or(ParseError::NoBTF)?; + let is_link = section.kind == EbpfSectionKind::StructOpsLink; + + // Get symbols for this section to find the variable names + let symbols = + self.symbols_by_section + .get(§ion.index) + .ok_or(ParseError::NoSymbolsForSection { + section_name: section.name.to_owned(), + })?; + + // Look for the DATASEC type for this section in BTF + let sec_name = section.name; + let datasec = btf.types().enumerate().find_map(|(i, t)| { + if let crate::btf::BtfType::DataSec(ds) = t { + let name = btf.string_at(ds.name_offset).ok()?; + if name == sec_name { + return Some((i as u32, ds)); + } + } + None + }); + + // If no DATASEC found in BTF, we can still create maps from symbols + // but won't have BTF type information + if let Some((_sec_type_id, datasec)) = datasec { + // Parse variables from BTF DATASEC + for var_sec_info in datasec.entries.iter() { + let var_type = btf.type_by_id(var_sec_info.btf_type)?; + + let var = match var_type { + crate::btf::BtfType::Var(v) => v, + _ => continue, // Skip non-VAR types + }; + + let var_name = btf.string_at(var.name_offset)?.to_string(); + + // Get the actual struct type that the VAR points to + let struct_type_id = var.btf_type; + let struct_type = btf.type_by_id(struct_type_id)?; + + let type_name = match struct_type { + crate::btf::BtfType::Struct(s) => btf.string_at(s.name_offset)?.to_string(), + _ => continue, // Skip if not a struct type + }; + + // Find the symbol index for this variable + let symbol_index = symbols + .iter() + .find(|&&sym_idx| { + if let Some(sym) = self.symbol_table.get(&sym_idx) { + if let Some(name) = sym.name.as_ref() { + return name == &var_name; + } + } + false + }) + .copied() + .unwrap_or(0); + + // Extract the data for this variable from the section + let offset = var_sec_info.offset as usize; + let size = var_sec_info.size as usize; + let data = if offset + size <= section.data.len() { + section.data[offset..offset + size].to_vec() + } else { + alloc::vec![0u8; size] + }; + + // Process relocations to find function pointer fields + // Relocations point to program symbols at specific offsets in the struct + let mut func_info = alloc::vec::Vec::new(); + for rel in section.relocations.iter() { + // Check if this relocation is within this variable's data + let rel_offset = rel.offset as usize; + if rel_offset >= offset && rel_offset < offset + size { + // Get the symbol for this relocation + if let Some(sym) = self.symbol_table.get(&rel.symbol_index) { + // Check if this is a text symbol (program) + if sym.kind == object::SymbolKind::Text { + if let Some(name) = sym.name.as_ref() { + // The member_offset is relative to the start of this struct + let member_offset = (rel_offset - offset) as u32; + func_info.push(crate::maps::StructOpsFuncInfo { + member_name: alloc::string::String::new(), // Could look up from BTF + member_offset, + prog_name: name.clone(), + }); + } + } + } + } + } + + // Create the StructOpsMap entry + let map = crate::maps::StructOpsMap { + type_name, + btf_type_id: struct_type_id, + section_index: section.index.0, + symbol_index, + data, + is_link, + func_info, + data_offset: 0, // Will be set from kernel BTF during loading + }; + + self.maps.insert(var_name, crate::maps::Map::StructOps(map)); + } + } + + Ok(()) + } + fn parse_section(&mut self, section: Section<'_>) -> Result<(), ParseError> { self.section_infos .insert(section.name.to_owned(), (section.index, section.size)); @@ -878,7 +1007,25 @@ impl Object { ); } } - EbpfSectionKind::Undefined | EbpfSectionKind::License | EbpfSectionKind::Version => {} + EbpfSectionKind::Undefined + | EbpfSectionKind::License + | EbpfSectionKind::Version + | EbpfSectionKind::Ksyms => {} + EbpfSectionKind::StructOps | EbpfSectionKind::StructOpsLink => { + self.parse_struct_ops_section(§ion)?; + // Store relocations for struct_ops sections - they'll be used to resolve + // function pointer fields to BPF programs + if !section.relocations.is_empty() { + self.relocations.insert( + section.index, + section + .relocations + .into_iter() + .map(|rel| (rel.offset, rel)) + .collect(), + ); + } + } } Ok(()) @@ -1033,6 +1180,12 @@ pub enum EbpfSectionKind { License, /// `version` Version, + /// `.struct_ops` + StructOps, + /// `.struct_ops.link` + StructOpsLink, + /// `.ksyms` - kernel symbol references resolved at load time + Ksyms, } impl EbpfSectionKind { @@ -1045,6 +1198,10 @@ impl EbpfSectionKind { Self::Maps } else if name.starts_with(".maps") { Self::BtfMaps + } else if name.starts_with(".struct_ops.link") { + Self::StructOpsLink + } else if name.starts_with(".struct_ops") { + Self::StructOps } else if name.starts_with(".text") { Self::Text } else if name.starts_with(".bss") { @@ -1057,6 +1214,8 @@ impl EbpfSectionKind { Self::Btf } else if name == ".BTF.ext" { Self::BtfExt + } else if name == ".ksyms" { + Self::Ksyms } else { Self::Undefined } @@ -2635,6 +2794,52 @@ mod tests { ); } + #[test] + fn test_parse_section_struct_ops() { + let mut obj = fake_obj(); + fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN); + + assert_matches!( + obj.parse_section(fake_section( + EbpfSectionKind::Program, + "struct_ops/foo", + bytes_of(&fake_ins()), + None + )), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::StructOps { sleepable: false, member_name }, + .. + }) if member_name == "foo" + ); + } + + #[test] + fn test_parse_section_struct_ops_sleepable() { + let mut obj = fake_obj(); + fake_sym(&mut obj, 0, 0, "foo", FAKE_INS_LEN); + + assert_matches!( + obj.parse_section(fake_section( + EbpfSectionKind::Program, + "struct_ops.s/foo", + bytes_of(&fake_ins()), + None + )), + Ok(()) + ); + assert_matches!( + obj.programs.get("foo"), + Some(Program { + section: ProgramSection::StructOps { sleepable: true, member_name }, + .. + }) if member_name == "foo" + ); + } + #[test] fn test_patch_map_data() { let mut obj = fake_obj(); diff --git a/aya-obj/src/relocation.rs b/aya-obj/src/relocation.rs index d035339b4..af299cf4e 100644 --- a/aya-obj/src/relocation.rs +++ b/aya-obj/src/relocation.rs @@ -8,9 +8,10 @@ use object::{SectionIndex, SymbolKind}; use crate::{ EbpfSectionKind, + btf::{Btf, BtfError, BtfKind}, generated::{ - BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_MAP_FD, - BPF_PSEUDO_MAP_VALUE, bpf_insn, + BPF_CALL, BPF_JMP, BPF_K, BPF_PSEUDO_CALL, BPF_PSEUDO_FUNC, BPF_PSEUDO_KFUNC_CALL, + BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE, bpf_insn, }, maps::Map, obj::{Function, Object}, @@ -85,6 +86,16 @@ pub enum RelocationError { /// The relocation number relocation_number: usize, }, + + /// Kfunc not found in kernel BTF + #[error("kfunc `{name}` not found in kernel BTF")] + UnknownKfunc { + /// The kfunc name + name: String, + /// The underlying BTF error + #[source] + error: BtfError, + }, } #[derive(Debug, Copy, Clone)] @@ -144,9 +155,14 @@ impl Object { } /// Relocates function calls + /// + /// If `kernel_btf` is provided, kfunc calls will be resolved by looking up + /// the function name in the kernel BTF and setting the instruction's `imm` + /// to the BTF type ID. pub fn relocate_calls( &mut self, text_sections: &HashSet, + kernel_btf: Option<&Btf>, ) -> Result<(), EbpfRelocationError> { for (name, program) in self.programs.iter() { let linker = FunctionLinker::new( @@ -154,6 +170,7 @@ impl Object { &self.relocations, &self.symbol_table, text_sections, + kernel_btf, ); let func_orig = @@ -281,6 +298,7 @@ struct FunctionLinker<'a> { relocations: &'a HashMap>, symbol_table: &'a HashMap, text_sections: &'a HashSet, + kernel_btf: Option<&'a Btf>, } impl<'a> FunctionLinker<'a> { @@ -289,6 +307,7 @@ impl<'a> FunctionLinker<'a> { relocations: &'a HashMap>, symbol_table: &'a HashMap, text_sections: &'a HashSet, + kernel_btf: Option<&'a Btf>, ) -> Self { Self { functions, @@ -296,6 +315,7 @@ impl<'a> FunctionLinker<'a> { relocations, symbol_table, text_sections, + kernel_btf, } } @@ -367,17 +387,52 @@ impl<'a> FunctionLinker<'a> { self.symbol_table .get(&rel.symbol_index) .map(|sym| (rel, sym)) - }) - .filter(|(_rel, sym)| { - // only consider text relocations, data relocations are - // relocated in relocate_maps() - sym.kind == SymbolKind::Text - || sym - .section_index - .map(|section_index| self.text_sections.contains(§ion_index)) - .unwrap_or(false) }); + // Check if this is a kfunc (external kernel function) relocation. + // Kfuncs are represented as undefined/external symbols in the ELF file, + // which means they have no section_index (sym.section_index.is_none()). + // These need to be resolved at load time by looking up the function + // in the kernel's BTF and setting the instruction's imm to the BTF ID. + if let Some((_rel, sym)) = &rel { + if sym.section_index.is_none() { + let kfunc_name = sym.name.as_deref().unwrap_or("?"); + + // Look up the kfunc in kernel BTF. If kernel BTF is not available, + // we cannot resolve the kfunc and must fail. + let btf = self.kernel_btf.ok_or_else(|| RelocationError::UnknownKfunc { + name: kfunc_name.to_owned(), + error: BtfError::NoBtf, + })?; + + let btf_id = btf + .id_by_type_name_kind(kfunc_name, BtfKind::Func) + .map_err(|error| RelocationError::UnknownKfunc { + name: kfunc_name.to_owned(), + error, + })?; + + // Set the instruction to call the kfunc via BTF + let ins = &mut program.instructions[ins_index]; + ins.imm = btf_id as i32; + ins.set_src_reg(BPF_PSEUDO_KFUNC_CALL as u8); + debug!( + "resolved kfunc `{kfunc_name}` to BTF id {btf_id} at instruction {ins_index}" + ); + continue; + } + } + + // Filter to only consider text relocations - data relocations are + // handled in relocate_maps() + let rel = rel.filter(|(_rel, sym)| { + sym.kind == SymbolKind::Text + || sym + .section_index + .map(|section_index| self.text_sections.contains(§ion_index)) + .unwrap_or(false) + }); + // not a call and not a text relocation, we don't need to do anything if !is_call && rel.is_none() { continue; diff --git a/aya/src/bpf.rs b/aya/src/bpf.rs index a7eef08d8..07c9f8502 100644 --- a/aya/src/bpf.rs +++ b/aya/src/bpf.rs @@ -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, + StructOps, Syscall, TracePoint, UProbe, Xdp, }, sys::{ bpf_load_btf, is_bpf_cookie_supported, is_bpf_global_data_supported, @@ -458,7 +458,11 @@ impl<'a> EbpfLoader<'a> { | ProgramSection::Lsm { sleepable: _ } | ProgramSection::LsmCgroup | ProgramSection::BtfTracePoint - | ProgramSection::Iter { sleepable: _ } => { + | ProgramSection::Iter { sleepable: _ } + | ProgramSection::StructOps { + sleepable: _, + member_name: _, + } => { return Err(EbpfError::BtfError(err)); } ProgramSection::KRetProbe @@ -488,7 +492,8 @@ impl<'a> EbpfLoader<'a> { | ProgramSection::SkLookup | ProgramSection::FlowDissector | ProgramSection::CgroupSock { attach_type: _ } - | ProgramSection::CgroupDevice => {} + | ProgramSection::CgroupDevice + | ProgramSection::Syscall => {} } } @@ -538,7 +543,38 @@ impl<'a> EbpfLoader<'a> { obj.set_value_size(value_size) } let btf_fd = btf_fd.as_deref().map(|fd| fd.as_fd()); - let mut map = if let Some(pin_path) = map_pin_path_by_name.get(name.as_str()) { + + // Handle struct_ops maps specially - they need btf_vmlinux_value_type_id + let mut map = if let aya_obj::Map::StructOps(ref struct_ops_map) = obj { + // Look up the kernel BTF type for bpf_struct_ops_ + let vmlinux_btf = btf.as_deref().ok_or_else(|| { + EbpfError::BtfError(aya_obj::btf::BtfError::UnknownBtfTypeName { + type_name: format!( + "bpf_struct_ops_{} (kernel BTF not available)", + struct_ops_map.type_name + ), + }) + })?; + let kernel_type_name = format!("bpf_struct_ops_{}", struct_ops_map.type_name); + let vmlinux_type_id = vmlinux_btf + .id_by_type_name_kind(&kernel_type_name, aya_obj::btf::BtfKind::Struct)?; + + // Get the kernel struct size - the kernel rejects maps with wrong value_size + let kernel_value_size = vmlinux_btf.type_size(vmlinux_type_id)? as u32; + + // Get the offset of the 'data' field within the wrapper struct + // The actual struct_ops data is stored at this offset + let data_offset = vmlinux_btf.struct_member_byte_offset(vmlinux_type_id, "data")?; + + MapData::create_struct_ops( + obj, + &name, + btf_fd, + vmlinux_type_id, + kernel_value_size, + data_offset, + )? + } else if let Some(pin_path) = map_pin_path_by_name.get(name.as_str()) { MapData::create_pinned_by_name(pin_path, obj, &name, btf_fd)? } else { match obj.pinning() { @@ -555,7 +591,11 @@ impl<'a> EbpfLoader<'a> { } } }; - map.finalize()?; + // Don't finalize struct_ops maps - they need program FDs filled in first + // and will be registered later via StructOpsMap::register() + if !matches!(map.obj(), aya_obj::Map::StructOps(_)) { + map.finalize()?; + } maps.insert(name, map); } @@ -570,7 +610,7 @@ impl<'a> EbpfLoader<'a> { .map(|(s, data)| (s.as_str(), data.fd().as_fd().as_raw_fd(), data.obj())), &text_sections, )?; - obj.relocate_calls(&text_sections)?; + obj.relocate_calls(&text_sections, btf.as_deref())?; obj.sanitize_functions(&FEATURES); let programs = obj @@ -750,6 +790,23 @@ impl<'a> EbpfLoader<'a> { } Program::Iter(Iter { data }) } + ProgramSection::StructOps { + sleepable, + member_name, + } => { + let mut data = + ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level); + if *sleepable { + data.flags = BPF_F_SLEEPABLE; + } + Program::StructOps(StructOps { + data, + member_name: member_name.clone(), + }) + } + ProgramSection::Syscall => Program::Syscall(Syscall { + data: ProgramData::new(prog_name, obj, btf_fd, *verifier_log_level), + }), } }; (name, program) @@ -792,6 +849,7 @@ fn parse_map( BPF_MAP_TYPE_DEVMAP_HASH => Map::DevMapHash(map), BPF_MAP_TYPE_XSKMAP => Map::XskMap(map), BPF_MAP_TYPE_SK_STORAGE => Map::SkStorage(map), + BPF_MAP_TYPE_STRUCT_OPS => Map::StructOps(map), m_type => { if allow_unsupported_maps { Map::Unsupported(map) @@ -860,6 +918,31 @@ fn adjust_to_page_size(byte_size: u32, page_size: u32) -> u32 { page_size * pages_needed.next_power_of_two() } +/// Writes a file descriptor at the specified offset in struct_ops map data. +/// +/// This helper handles the offset calculation (data_offset + member_offset) and +/// bounds checking for writing program FDs into struct_ops map data buffers. +/// +/// # Arguments +/// +/// * `data` - The mutable data buffer to write into +/// * `data_offset` - The byte offset of the ops struct within the kernel wrapper struct +/// * `member_offset` - The byte offset of the member within the ops struct +/// * `fd` - The file descriptor value to write (as i32) +/// +/// # Returns +/// +/// `true` if the write succeeded, `false` if the calculated offset is out of bounds. +fn write_struct_ops_fd(data: &mut [u8], data_offset: u32, member_offset: u32, fd: i32) -> bool { + let actual_offset = (data_offset + member_offset) as usize; + if actual_offset + 4 <= data.len() { + data[actual_offset..actual_offset + 4].copy_from_slice(&fd.to_ne_bytes()); + true + } else { + false + } +} + #[cfg(test)] mod tests { use aya_obj::generated::bpf_map_type::*; @@ -913,6 +996,58 @@ mod tests { ) }) } + + mod struct_ops_fd { + use super::super::write_struct_ops_fd; + + #[test] + fn test_write_with_both_offsets() { + let mut data = vec![0u8; 64]; + let fd: i32 = 789; + + // data_offset=8, member_offset=16 -> writes at byte 24 + let result = write_struct_ops_fd(&mut data, 8, 16, fd); + + assert!(result); + assert_eq!(i32::from_ne_bytes(data[24..28].try_into().unwrap()), 789); + } + + #[test] + fn test_write_out_of_bounds_returns_false() { + let mut data = vec![0u8; 16]; + + // Trying to write at offset 20 in a 16-byte buffer + let result = write_struct_ops_fd(&mut data, 0, 20, 42); + + assert!(!result); + // Data should be unchanged + assert!(data.iter().all(|&b| b == 0)); + } + + #[test] + fn test_write_partial_out_of_bounds() { + let mut data = vec![0u8; 16]; + + // Offset 14 + 4 bytes = 18, which exceeds buffer size of 16 + let result = write_struct_ops_fd(&mut data, 0, 14, 42); + + assert!(!result); + // Data should be unchanged + assert!(data.iter().all(|&b| b == 0)); + } + + #[test] + fn test_write_at_exact_boundary() { + let mut data = vec![0u8; 16]; + let fd: i32 = 999; + + // Offset 12 + 4 bytes = 16, exactly at buffer boundary (valid) + let result = write_struct_ops_fd(&mut data, 0, 12, fd); + + assert!(result); + assert_eq!(i32::from_ne_bytes(data[12..16].try_into().unwrap()), 999); + } + } } impl Default for EbpfLoader<'_> { @@ -1171,6 +1306,166 @@ impl Ebpf { pub fn programs_mut(&mut self) -> impl Iterator { self.programs.iter_mut().map(|(s, p)| (s.as_str(), p)) } + + /// Loads struct_ops programs and fills their file descriptors into the corresponding maps. + /// + /// This method must be called before registering struct_ops maps. It: + /// 1. Loads all struct_ops programs + /// 2. Gets their file descriptors + /// 3. Writes the FDs to the correct offsets in the struct_ops map data + /// + /// After calling this method, you can register the struct_ops map using + /// [`StructOpsMap::register`](crate::maps::StructOpsMap::register). + /// + /// # Arguments + /// + /// * `btf` - The kernel BTF information, typically from `Btf::from_sys_fs()` + /// + /// # Example + /// + /// ```no_run + /// use aya::{Ebpf, Btf, maps::StructOpsMap}; + /// + /// let mut bpf = Ebpf::load_file("my_struct_ops.o")?; + /// let btf = Btf::from_sys_fs()?; + /// bpf.load_struct_ops(&btf)?; + /// + /// let mut struct_ops: StructOpsMap<_> = bpf.take_map("my_ops").unwrap().try_into()?; + /// struct_ops.register()?; + /// # Ok::<(), Box>(()) + /// ``` + pub fn load_struct_ops(&mut self, btf: &Btf) -> Result<(), EbpfError> { + use std::os::fd::{AsFd as _, AsRawFd as _}; + + use aya_obj::btf::BtfKind; + + // Collect (map_name, struct_name, program_names) for each struct_ops map + let mut struct_ops_info: Vec<(String, String, Vec)> = Vec::new(); + for (map_name, map) in self.maps.iter() { + if let Map::StructOps(map_data) = map { + if let aya_obj::Map::StructOps(m) = map_data.obj() { + let prog_names: Vec = + m.func_info.iter().map(|i| i.prog_name.clone()).collect(); + debug!( + "struct_ops map '{map_name}' type='{}' prog_names={prog_names:?}", + m.type_name + ); + struct_ops_info.push((map_name.clone(), m.type_name.clone(), prog_names)); + } + } + } + + // If func_info is empty (no relocations), fall back to matching by member_name + // This happens with Rust BPF objects where function pointer fields can't create relocations + let mut fallback_matches: Vec<(String, String, String, u32)> = Vec::new(); // (map_name, struct_name, prog_name, member_offset) + for (map_name, struct_name, prog_names) in &struct_ops_info { + if prog_names.is_empty() { + debug!( + "struct_ops map '{map_name}' has no func_info, using fallback matching by member_name" + ); + // Get struct BTF type ID for member offset lookup + if let Ok(struct_type_id) = btf.id_by_type_name_kind(struct_name, BtfKind::Struct) { + // Find all StructOps programs and match by member_name + for (prog_name, prog) in self.programs.iter() { + if let Program::StructOps(struct_ops) = prog { + let member_name = struct_ops.member_name(); + if let Ok(member_offset) = + btf.struct_member_byte_offset(struct_type_id, member_name) + { + debug!( + "fallback: matched program '{prog_name}' to member '{member_name}' at offset {member_offset}" + ); + fallback_matches.push(( + map_name.clone(), + struct_name.clone(), + prog_name.clone(), + member_offset, + )); + } + } + } + } + } + } + + // Load all struct_ops programs (from func_info) + for (_, struct_name, prog_names) in &struct_ops_info { + for prog_name in prog_names { + debug!("looking for struct_ops program '{prog_name}'"); + if let Some(Program::StructOps(struct_ops)) = self.programs.get_mut(prog_name) { + debug!("loading struct_ops program '{prog_name}' for struct '{struct_name}'"); + struct_ops.load(struct_name, btf)?; + } else { + warn!("struct_ops program '{prog_name}' not found or not StructOps type"); + } + } + } + + // Load fallback-matched programs + for (_, struct_name, prog_name, _) in &fallback_matches { + if let Some(Program::StructOps(struct_ops)) = self.programs.get_mut(prog_name) { + debug!( + "loading fallback struct_ops program '{prog_name}' for struct '{struct_name}'" + ); + struct_ops.load(struct_name, btf)?; + } + } + + // Fill program FDs into struct_ops maps + for (map_name, map) in self.maps.iter_mut() { + if let Map::StructOps(map_data) = map { + // Get the func_info and data_offset from the map's object + let (func_info, data_offset): (Vec<_>, u32) = { + if let aya_obj::Map::StructOps(m) = map_data.obj() { + (m.func_info.clone(), m.data_offset) + } else { + continue; + } + }; + + debug!("struct_ops map '{map_name}' data_offset={data_offset}"); + + // Fill FDs from func_info (relocation-based) + for info in &func_info { + if let Some(Program::StructOps(prog)) = self.programs.get(&info.prog_name) { + let fd = prog.fd()?; + let raw_fd = fd.as_fd().as_raw_fd(); + let member_offset = info.member_offset; + let prog_name = &info.prog_name; + let actual_offset = data_offset + member_offset; + debug!( + "filling FD {raw_fd} for program '{prog_name}' at offset {actual_offset} (data_offset {data_offset} + member_offset {member_offset})" + ); + let data = map_data.obj_mut().data_mut(); + write_struct_ops_fd(data, data_offset, member_offset, raw_fd); + } else { + let prog_name = &info.prog_name; + warn!("struct_ops program '{prog_name}' not found"); + } + } + + // Fill FDs from fallback matches (section-name based) + if func_info.is_empty() { + for (fallback_map, _, prog_name, member_offset) in &fallback_matches { + if fallback_map == map_name { + if let Some(Program::StructOps(prog)) = self.programs.get(prog_name) { + let fd = prog.fd()?; + let raw_fd = fd.as_fd().as_raw_fd(); + let actual_offset = data_offset + member_offset; + debug!( + "filling FD {raw_fd} for fallback program '{prog_name}' at offset {actual_offset} (data_offset {data_offset} + member_offset {member_offset})" + ); + let data = map_data.obj_mut().data_mut(); + write_struct_ops_fd(data, data_offset, *member_offset, raw_fd); + } + } + } + } + } + } + + Ok(()) + } } /// The error type returned by [`Ebpf::load_file`] and [`Ebpf::load`]. diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 9e65c0cce..64ed777fc 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -66,8 +66,9 @@ use crate::{ PinningType, Pod, pin::PinError, sys::{ - SyscallError, bpf_create_map, bpf_get_object, bpf_map_freeze, bpf_map_get_fd_by_id, - bpf_map_get_next_key, bpf_map_update_elem_ptr, bpf_pin_object, + SyscallError, bpf_create_map, bpf_create_map_with_vmlinux_btf, bpf_get_object, + bpf_map_freeze, bpf_map_get_fd_by_id, bpf_map_get_next_key, bpf_map_update_elem_ptr, + bpf_pin_object, }, util::nr_cpus, }; @@ -84,6 +85,7 @@ pub mod sk_storage; pub mod sock; pub mod stack; pub mod stack_trace; +pub mod struct_ops; pub mod xdp; pub use array::{Array, PerCpuArray, ProgramArray}; @@ -98,6 +100,7 @@ pub use sk_storage::SkStorage; pub use sock::{SockHash, SockMap}; pub use stack::Stack; pub use stack_trace::StackTraceMap; +pub use struct_ops::StructOpsMap; pub use xdp::{CpuMap, DevMap, DevMapHash, XskMap}; #[derive(Error, Debug)] @@ -275,6 +278,8 @@ pub enum Map { Stack(MapData), /// A [`StackTraceMap`] map. StackTraceMap(MapData), + /// A [`StructOpsMap`] map. + StructOps(MapData), /// An unsupported map type. Unsupported(MapData), /// A [`XskMap`] map. @@ -305,6 +310,7 @@ impl Map { Self::SkStorage(map) => map.obj.map_type(), Self::Stack(map) => map.obj.map_type(), Self::StackTraceMap(map) => map.obj.map_type(), + Self::StructOps(map) => map.obj.map_type(), Self::Unsupported(map) => map.obj.map_type(), Self::XskMap(map) => map.obj.map_type(), } @@ -336,6 +342,7 @@ impl Map { Self::SkStorage(map) => map.pin(path), Self::Stack(map) => map.pin(path), Self::StackTraceMap(map) => map.pin(path), + Self::StructOps(map) => map.pin(path), Self::Unsupported(map) => map.pin(path), Self::XskMap(map) => map.pin(path), } @@ -380,7 +387,7 @@ impl Map { bpf_map_type::BPF_MAP_TYPE_CGROUP_STORAGE_DEPRECATED => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_REUSEPORT_SOCKARRAY => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_SK_STORAGE => Self::SkStorage(map_data), - bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS => Self::Unsupported(map_data), + bpf_map_type::BPF_MAP_TYPE_STRUCT_OPS => Self::StructOps(map_data), bpf_map_type::BPF_MAP_TYPE_INODE_STORAGE => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_TASK_STORAGE => Self::Unsupported(map_data), bpf_map_type::BPF_MAP_TYPE_USER_RINGBUF => Self::Unsupported(map_data), @@ -426,6 +433,7 @@ impl_map_pin!(() { ProgramArray, SockMap, StackTraceMap, + StructOpsMap, CpuMap, DevMap, DevMapHash, @@ -508,6 +516,7 @@ impl_try_from_map!(() { RingBuf, SockMap, StackTraceMap, + StructOpsMap from StructOps, XskMap, }); @@ -607,6 +616,48 @@ impl MapData { }) } + /// Creates a struct_ops map with the btf_vmlinux_value_type_id. + /// + /// This is used for BPF_MAP_TYPE_STRUCT_OPS maps which require the kernel BTF + /// type ID for the struct_ops type. + pub(crate) fn create_struct_ops( + mut obj: aya_obj::Map, + name: &str, + btf_fd: Option>, + btf_vmlinux_value_type_id: u32, + kernel_value_size: u32, + data_offset: u32, + ) -> Result { + let c_name = CString::new(name) + .map_err(|std::ffi::NulError { .. }| MapError::InvalidName { name: name.into() })?; + + // For struct_ops maps, value_size must match kernel wrapper size (bpf_struct_ops_) + // The .struct_ops section data represents the ops struct, which must be placed at + // data_offset within the kernel wrapper struct (bpf_struct_ops_). + if let aya_obj::Map::StructOps(ref mut m) = obj { + let original_data = std::mem::take(&mut m.data); + let mut new_data = vec![0u8; kernel_value_size as usize]; + let offset = data_offset as usize; + // Copy original data to the correct offset within the wrapper struct + if offset + original_data.len() <= new_data.len() { + new_data[offset..offset + original_data.len()].copy_from_slice(&original_data); + } + m.data = new_data; + m.data_offset = data_offset; + } + + let fd = + bpf_create_map_with_vmlinux_btf(&c_name, &obj, btf_fd, Some(btf_vmlinux_value_type_id)) + .map_err(|io_error| MapError::CreateError { + name: name.into(), + io_error, + })?; + Ok(Self { + obj, + fd: MapFd::from_fd(fd), + }) + } + pub(crate) fn create_pinned_by_name>( path: P, obj: aya_obj::Map, @@ -768,6 +819,11 @@ impl MapData { obj } + pub(crate) fn obj_mut(&mut self) -> &mut aya_obj::Map { + let Self { obj, fd: _ } = self; + obj + } + /// Returns the kernel's information about the loaded map. pub fn info(&self) -> Result { MapInfo::new_from_fd(self.fd.as_fd()) diff --git a/aya/src/maps/struct_ops.rs b/aya/src/maps/struct_ops.rs new file mode 100644 index 000000000..a88e1a933 --- /dev/null +++ b/aya/src/maps/struct_ops.rs @@ -0,0 +1,514 @@ +//! A struct_ops map for implementing kernel callbacks. +//! +//! Struct_ops maps are special maps that allow implementing kernel subsystem callbacks +//! (like tcp_congestion_ops, hid_bpf_ops, sched_ext_ops) in BPF programs. +//! +//! Unlike regular maps that store key-value pairs, struct_ops maps represent a registration +//! mechanism for kernel callbacks. The "value" is the struct_ops structure with program FDs +//! filled in at the appropriate offsets. + +use std::{ + borrow::{Borrow, BorrowMut}, + os::fd::AsFd as _, +}; + +use aya_obj::maps::StructOpsFuncInfo; +use log::debug; + +use crate::{ + maps::{MapData, MapError, MapFd}, + programs::links::FdLink, + sys::{SyscallError, bpf_map_update_elem_ptr, bpf_struct_ops_link_create}, +}; + +/// A struct_ops map that implements kernel callbacks. +/// +/// Struct_ops maps are used to register BPF programs as implementations of kernel +/// callbacks like `hid_bpf_ops` for HID device handling or `sched_ext_ops` for +/// custom schedulers. +/// +/// # Example +/// +/// ```no_run +/// # #[derive(thiserror::Error, Debug)] +/// # enum Error { +/// # #[error(transparent)] +/// # BtfError(#[from] aya::BtfError), +/// # #[error(transparent)] +/// # Map(#[from] aya::maps::MapError), +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Ebpf(#[from] aya::EbpfError), +/// # } +/// # let mut bpf = aya::Ebpf::load_file("ebpf_programs.o")?; +/// use aya::maps::StructOpsMap; +/// use aya::Btf; +/// +/// // Get the struct_ops map +/// let struct_ops: StructOpsMap<_> = bpf.map("my_struct_ops").unwrap().try_into()?; +/// +/// # Ok::<(), Error>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_MAP_TYPE_STRUCT_OPS")] +pub struct StructOpsMap { + pub(crate) inner: T, +} + +impl> StructOpsMap { + pub(crate) fn new(map: T) -> Result { + let data = map.borrow(); + let _map_type = data.obj.map_type(); + + Ok(Self { inner: map }) + } + + /// Returns the file descriptor of the map. + pub fn fd(&self) -> &MapFd { + self.inner.borrow().fd() + } + + /// Returns information about the function pointer fields in this struct_ops. + /// + /// Each entry contains: + /// - `member_name`: The name of the function pointer field in the struct + /// - `member_offset`: The byte offset where the program FD should be written + /// - `prog_name`: The name of the BPF program that implements this callback + pub fn func_info(&self) -> Option<&[StructOpsFuncInfo]> { + let data = self.inner.borrow(); + if let aya_obj::Map::StructOps(m) = data.obj() { + Some(&m.func_info) + } else { + None + } + } + + /// Returns the type name of this struct_ops (e.g., "hid_bpf_ops"). + pub fn type_name(&self) -> Option<&str> { + let data = self.inner.borrow(); + if let aya_obj::Map::StructOps(m) = data.obj() { + Some(&m.type_name) + } else { + None + } + } + + /// Returns whether this is a link-based struct_ops. + pub fn is_link(&self) -> bool { + let data = self.inner.borrow(); + if let aya_obj::Map::StructOps(m) = data.obj() { + m.is_link + } else { + false + } + } +} + +impl> StructOpsMap { + /// Sets a program FD at the specified offset in the struct data. + /// + /// This is an internal method used by [`Ebpf::load_struct_ops`] to fill in + /// program FDs for function pointer fields. Users should call + /// `load_struct_ops()` instead of this method directly. + /// + /// [`Ebpf::load_struct_ops`]: crate::Ebpf::load_struct_ops + #[allow(dead_code)] + pub(crate) fn set_prog_fd(&mut self, offset: u32, fd: i32) -> Result<(), MapError> { + self.set_field_i32(offset, fd) + } + + /// Internal helper to write bytes at an offset in the struct data. + /// + /// Handles the data_offset adjustment for struct_ops maps and bounds checking. + fn write_bytes_at_offset(&mut self, offset: u32, bytes: &[u8]) -> Result<(), MapError> { + let map_data = self.inner.borrow_mut(); + let obj = map_data.obj_mut(); + + // Get the data_offset from the struct_ops map - this is the offset of the + // actual ops struct within the kernel wrapper struct + let data_offset = if let aya_obj::Map::StructOps(m) = obj { + m.data_offset + } else { + 0 + }; + + let data = obj.data_mut(); + // Add data_offset to the user-provided offset + let actual_offset = (data_offset + offset) as usize; + if actual_offset + bytes.len() > data.len() { + return Err(MapError::OutOfBounds { + index: actual_offset as u32, + max_entries: data.len() as u32, + }); + } + + data[actual_offset..actual_offset + bytes.len()].copy_from_slice(bytes); + Ok(()) + } + + /// Sets an i32 field at the specified byte offset in the struct data. + /// + /// This is useful for setting fields like `hid_id` in HID-BPF struct_ops + /// before registration. + /// + /// # Arguments + /// + /// * `offset` - The byte offset where the value should be written + /// * `value` - The i32 value to write + /// + /// # Example + /// + /// ```no_run + /// # use aya::maps::StructOpsMap; + /// # fn example(struct_ops: &mut StructOpsMap) -> Result<(), aya::maps::MapError> { + /// // Set hid_id at offset 0 before registration + /// struct_ops.set_field_i32(0, 189)?; // hid_id = 189 + /// # Ok(()) + /// # } + /// ``` + pub fn set_field_i32(&mut self, offset: u32, value: i32) -> Result<(), MapError> { + self.write_bytes_at_offset(offset, &value.to_ne_bytes()) + } + + /// Sets a u32 field at the specified byte offset in the struct data. + /// + /// # Arguments + /// + /// * `offset` - The byte offset where the value should be written + /// * `value` - The u32 value to write + /// + /// # Example + /// + /// ```no_run + /// # use aya::maps::StructOpsMap; + /// # fn example(struct_ops: &mut StructOpsMap) -> Result<(), aya::maps::MapError> { + /// // Set flags at offset 4 + /// struct_ops.set_field_u32(4, 0)?; + /// # Ok(()) + /// # } + /// ``` + pub fn set_field_u32(&mut self, offset: u32, value: u32) -> Result<(), MapError> { + self.write_bytes_at_offset(offset, &value.to_ne_bytes()) + } +} + +impl> StructOpsMap { + /// Registers the struct_ops by calling bpf_map_update_elem. + /// + /// This should be called after [`Ebpf::load_struct_ops`] has been called to load + /// all associated BPF programs and fill their file descriptors into the struct data. + /// + /// [`Ebpf::load_struct_ops`]: crate::Ebpf::load_struct_ops + pub fn register(&self) -> Result<(), MapError> { + let data = self.inner.borrow(); + let key: u32 = 0; + let fd = data.fd().as_fd(); + + let data_offset = if let aya_obj::Map::StructOps(m) = data.obj() { + m.data_offset + } else { + 0 + }; + + debug!( + "registering struct_ops map: data_len={} data_offset={}", + data.obj().data().len(), + data_offset + ); + + // For struct_ops, we need to update the map with the struct data. + // The struct data should already have program FDs filled in. + // Use a mutable copy to avoid casting away const from immutable data. + let mut value = data.obj().data().to_vec(); + bpf_map_update_elem_ptr(fd, &key, value.as_mut_ptr(), 0) + .map_err(|io_error| SyscallError { + call: "bpf_map_update_elem", + io_error, + }) + .map_err(MapError::from)?; + + Ok(()) + } + + /// Attaches a link-based struct_ops by creating a BPF link. + /// + /// For struct_ops maps created with the `BPF_F_LINK` flag (from `.struct_ops.link` section), + /// this method creates a BPF link that activates the struct_ops. This must be called + /// after [`register()`](Self::register) has been called to update the map data. + /// + /// For non-link struct_ops (from `.struct_ops` section), calling [`register()`](Self::register) + /// alone is sufficient - no link is needed. + /// + /// # Returns + /// + /// An `FdLink` that keeps the struct_ops active. When the link is dropped (or its + /// file descriptor is closed), the struct_ops will be detached. + /// + /// # Example + /// + /// ```no_run + /// # use aya::maps::StructOpsMap; + /// # fn example(struct_ops: &StructOpsMap) -> Result<(), aya::maps::MapError> { + /// // First register the struct_ops data + /// struct_ops.register()?; + /// + /// // For link-based struct_ops, create the link to activate + /// if struct_ops.is_link() { + /// let _link = struct_ops.attach()?; + /// // Keep the link alive to maintain attachment + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn attach(&self) -> Result { + let data = self.inner.borrow(); + let fd = data.fd().as_fd(); + + debug!("creating struct_ops link for map"); + + let link_fd = bpf_struct_ops_link_create(fd) + .map_err(|io_error| SyscallError { + call: "bpf_link_create", + io_error, + }) + .map_err(MapError::from)?; + + Ok(FdLink::new(link_fd)) + } +} + +impl TryFrom for StructOpsMap { + type Error = MapError; + + fn try_from(map: MapData) -> Result { + Self::new(map) + } +} + +#[cfg(test)] +mod tests { + use std::io; + + use assert_matches::assert_matches; + use aya_obj::generated::{bpf_cmd, bpf_map_type::BPF_MAP_TYPE_ARRAY}; + use libc::EFAULT; + + use super::*; + use crate::{ + maps::{Map, test_utils::new_map}, + sys::{SysResult, Syscall, override_syscall}, + }; + + fn sys_error(value: i32) -> SysResult { + Err((-1, io::Error::from_raw_os_error(value))) + } + + /// Creates a new aya_obj::Map::StructOps with the given parameters. + fn new_struct_ops_obj_map( + type_name: &str, + data_size: usize, + is_link: bool, + func_info: Vec, + data_offset: u32, + ) -> aya_obj::Map { + aya_obj::Map::StructOps(aya_obj::maps::StructOpsMap { + type_name: type_name.to_string(), + btf_type_id: 1, + section_index: 0, + symbol_index: 0, + data: vec![0u8; data_size], + is_link, + func_info, + data_offset, + }) + } + + /// Creates a basic struct_ops map for testing. + fn new_basic_struct_ops_obj_map() -> aya_obj::Map { + new_struct_ops_obj_map("test_ops", 64, false, vec![], 0) + } + + #[test] + fn test_try_from_ok() { + let map = new_map(new_basic_struct_ops_obj_map()); + let map = Map::StructOps(map); + assert!(StructOpsMap::try_from(&map).is_ok()); + } + + #[test] + fn test_try_from_wrong_map() { + let map = new_map(crate::maps::test_utils::new_obj_map::(BPF_MAP_TYPE_ARRAY)); + let map = Map::Array(map); + + assert_matches!( + StructOpsMap::try_from(&map), + Err(MapError::InvalidMapType { .. }) + ); + } + + #[test] + fn test_func_info() { + let func_info = vec![ + aya_obj::maps::StructOpsFuncInfo { + member_name: "callback1".to_string(), + member_offset: 8, + prog_name: "my_prog1".to_string(), + }, + aya_obj::maps::StructOpsFuncInfo { + member_name: "callback2".to_string(), + member_offset: 16, + prog_name: "my_prog2".to_string(), + }, + ]; + let map = new_map(new_struct_ops_obj_map("test_ops", 64, false, func_info, 0)); + let struct_ops = StructOpsMap::new(&map).unwrap(); + + let info = struct_ops.func_info().unwrap(); + assert_eq!(info.len(), 2); + assert_eq!(info[0].member_name, "callback1"); + assert_eq!(info[0].member_offset, 8); + assert_eq!(info[0].prog_name, "my_prog1"); + assert_eq!(info[1].member_name, "callback2"); + assert_eq!(info[1].member_offset, 16); + assert_eq!(info[1].prog_name, "my_prog2"); + } + + #[test] + fn test_type_name() { + let map = new_map(new_struct_ops_obj_map("hid_bpf_ops", 64, false, vec![], 0)); + let struct_ops = StructOpsMap::new(&map).unwrap(); + + assert_eq!(struct_ops.type_name(), Some("hid_bpf_ops")); + } + + #[test] + fn test_is_link_true() { + let map = new_map(new_struct_ops_obj_map("test_ops", 64, true, vec![], 0)); + let struct_ops = StructOpsMap::new(&map).unwrap(); + + assert!(struct_ops.is_link()); + } + + #[test] + fn test_is_link_false() { + let map = new_map(new_struct_ops_obj_map("test_ops", 64, false, vec![], 0)); + let struct_ops = StructOpsMap::new(&map).unwrap(); + + assert!(!struct_ops.is_link()); + } + + #[test] + fn test_set_field_i32_ok() { + let mut map = new_map(new_basic_struct_ops_obj_map()); + let mut struct_ops = StructOpsMap::new(&mut map).unwrap(); + + assert!(struct_ops.set_field_i32(0, 42).is_ok()); + + // Verify the value was written correctly + let data = map.obj().data(); + let value = i32::from_ne_bytes(data[0..4].try_into().unwrap()); + assert_eq!(value, 42); + } + + #[test] + fn test_set_field_out_of_bounds() { + let mut map = new_map(new_struct_ops_obj_map("test_ops", 16, false, vec![], 0)); + let mut struct_ops = StructOpsMap::new(&mut map).unwrap(); + + // Try to write at offset 20, which exceeds the 16 byte data size + assert_matches!( + struct_ops.set_field_i32(20, 42), + Err(MapError::OutOfBounds { .. }) + ); + } + + #[test] + fn test_set_field_with_data_offset() { + // Create a map with data_offset = 8 + let mut map = new_map(new_struct_ops_obj_map("test_ops", 64, false, vec![], 8)); + let mut struct_ops = StructOpsMap::new(&mut map).unwrap(); + + // Write at user offset 0, which should go to actual offset 8 + assert!(struct_ops.set_field_i32(0, 99).is_ok()); + + // Verify the value was written at the correct actual offset (8) + let data = map.obj().data(); + let value_at_0 = i32::from_ne_bytes(data[0..4].try_into().unwrap()); + let value_at_8 = i32::from_ne_bytes(data[8..12].try_into().unwrap()); + assert_eq!(value_at_0, 0); // Should be unchanged + assert_eq!(value_at_8, 99); // Should have the written value + } + + #[test] + fn test_set_field_with_data_offset_out_of_bounds() { + // Create a map with data_offset = 60, data size = 64 + // Writing 4 bytes at user offset 4 would go to actual offset 64, which is out of bounds + let mut map = new_map(new_struct_ops_obj_map("test_ops", 64, false, vec![], 60)); + let mut struct_ops = StructOpsMap::new(&mut map).unwrap(); + + assert_matches!( + struct_ops.set_field_i32(4, 42), + Err(MapError::OutOfBounds { .. }) + ); + } + + #[test] + fn test_register_ok() { + let map = new_map(new_basic_struct_ops_obj_map()); + let struct_ops = StructOpsMap::new(&map).unwrap(); + + override_syscall(|call| match call { + Syscall::Ebpf { + cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM, + .. + } => Ok(0), + _ => sys_error(EFAULT), + }); + + assert!(struct_ops.register().is_ok()); + } + + #[test] + fn test_register_syscall_error() { + let map = new_map(new_basic_struct_ops_obj_map()); + let struct_ops = StructOpsMap::new(&map).unwrap(); + + override_syscall(|_| sys_error(EFAULT)); + + assert_matches!( + struct_ops.register(), + Err(MapError::SyscallError(SyscallError { call: "bpf_map_update_elem", io_error })) if io_error.raw_os_error() == Some(EFAULT) + ); + } + + #[test] + fn test_attach_ok() { + let map = new_map(new_struct_ops_obj_map("test_ops", 64, true, vec![], 0)); + let struct_ops = StructOpsMap::new(&map).unwrap(); + + override_syscall(|call| match call { + Syscall::Ebpf { + cmd: bpf_cmd::BPF_LINK_CREATE, + .. + } => Ok(crate::MockableFd::mock_signed_fd().into()), + _ => sys_error(EFAULT), + }); + + let link = struct_ops.attach(); + assert!(link.is_ok()); + } + + #[test] + fn test_attach_syscall_error() { + let map = new_map(new_struct_ops_obj_map("test_ops", 64, true, vec![], 0)); + let struct_ops = StructOpsMap::new(&map).unwrap(); + + override_syscall(|_| sys_error(EFAULT)); + + assert_matches!( + struct_ops.attach(), + Err(MapError::SyscallError(SyscallError { call: "bpf_link_create", io_error })) if io_error.raw_os_error() == Some(EFAULT) + ); + } +} diff --git a/aya/src/programs/cgroup_skb.rs b/aya/src/programs/cgroup_skb.rs index 28f053bbe..888b62719 100644 --- a/aya/src/programs/cgroup_skb.rs +++ b/aya/src/programs/cgroup_skb.rs @@ -10,8 +10,9 @@ use aya_obj::generated::{ use crate::{ VerifierLogLevel, programs::{ - CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramType, - define_link_wrapper, id_as_key, impl_try_into_fdlink, load_program, + CgroupAttachMode, ExpectedAttachType, FdLink, Link, ProgAttachLink, ProgramData, + ProgramError, ProgramType, define_link_wrapper, id_as_key, impl_try_into_fdlink, + load_program, }, sys::{LinkTarget, SyscallError, bpf_link_create}, util::KernelVersion, @@ -66,10 +67,13 @@ impl CgroupSkb { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = self.attach_type.map(|attach_type| match attach_type { - CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS, - CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS, - }); + self.data.expected_attach_type = + self.attach_type + .map(|attach_type| match attach_type { + CgroupSkbAttachType::Ingress => BPF_CGROUP_INET_INGRESS, + CgroupSkbAttachType::Egress => BPF_CGROUP_INET_EGRESS, + }) + .map(ExpectedAttachType::AttachType); load_program(BPF_PROG_TYPE_CGROUP_SKB, &mut self.data) } diff --git a/aya/src/programs/cgroup_sock.rs b/aya/src/programs/cgroup_sock.rs index 4b1b768e7..f2cfd40a1 100644 --- a/aya/src/programs/cgroup_sock.rs +++ b/aya/src/programs/cgroup_sock.rs @@ -8,8 +8,9 @@ pub use aya_obj::programs::CgroupSockAttachType; use crate::{ VerifierLogLevel, programs::{ - CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramType, - define_link_wrapper, id_as_key, impl_try_into_fdlink, load_program, + CgroupAttachMode, ExpectedAttachType, FdLink, Link, ProgAttachLink, ProgramData, + ProgramError, ProgramType, define_link_wrapper, id_as_key, impl_try_into_fdlink, + load_program, }, sys::{LinkTarget, SyscallError, bpf_link_create}, util::KernelVersion, @@ -63,7 +64,8 @@ impl CgroupSock { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(self.attach_type.into()); + self.data.expected_attach_type = + Some(ExpectedAttachType::AttachType(self.attach_type.into())); load_program(BPF_PROG_TYPE_CGROUP_SOCK, &mut self.data) } @@ -78,7 +80,7 @@ impl CgroupSock { let prog_fd = self.fd()?; let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); - let attach_type = self.data.expected_attach_type.unwrap(); + let attach_type = self.data.expected_attach_type.unwrap().attach_type(); if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, diff --git a/aya/src/programs/cgroup_sock_addr.rs b/aya/src/programs/cgroup_sock_addr.rs index 1a11abfb0..9fd72d55b 100644 --- a/aya/src/programs/cgroup_sock_addr.rs +++ b/aya/src/programs/cgroup_sock_addr.rs @@ -8,8 +8,9 @@ pub use aya_obj::programs::CgroupSockAddrAttachType; use crate::{ VerifierLogLevel, programs::{ - CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramType, - define_link_wrapper, id_as_key, impl_try_into_fdlink, load_program, + CgroupAttachMode, ExpectedAttachType, FdLink, Link, ProgAttachLink, ProgramData, + ProgramError, ProgramType, define_link_wrapper, id_as_key, impl_try_into_fdlink, + load_program, }, sys::{LinkTarget, SyscallError, bpf_link_create}, util::KernelVersion, @@ -64,7 +65,8 @@ impl CgroupSockAddr { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(self.attach_type.into()); + self.data.expected_attach_type = + Some(ExpectedAttachType::AttachType(self.attach_type.into())); load_program(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, &mut self.data) } @@ -79,7 +81,7 @@ impl CgroupSockAddr { let prog_fd = self.fd()?; let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); - let attach_type = self.data.expected_attach_type.unwrap(); + let attach_type = self.data.expected_attach_type.unwrap().attach_type(); if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, diff --git a/aya/src/programs/cgroup_sockopt.rs b/aya/src/programs/cgroup_sockopt.rs index 2bd9d6d7f..5e28429b9 100644 --- a/aya/src/programs/cgroup_sockopt.rs +++ b/aya/src/programs/cgroup_sockopt.rs @@ -8,8 +8,8 @@ pub use aya_obj::programs::CgroupSockoptAttachType; use crate::{ VerifierLogLevel, programs::{ - CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramType, - define_link_wrapper, id_as_key, load_program, + CgroupAttachMode, ExpectedAttachType, FdLink, Link, ProgAttachLink, ProgramData, + ProgramError, ProgramType, define_link_wrapper, id_as_key, load_program, }, sys::{LinkTarget, SyscallError, bpf_link_create}, util::KernelVersion, @@ -61,7 +61,8 @@ impl CgroupSockopt { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(self.attach_type.into()); + self.data.expected_attach_type = + Some(ExpectedAttachType::AttachType(self.attach_type.into())); load_program(BPF_PROG_TYPE_CGROUP_SOCKOPT, &mut self.data) } @@ -76,7 +77,7 @@ impl CgroupSockopt { let prog_fd = self.fd()?; let prog_fd = prog_fd.as_fd(); let cgroup_fd = cgroup.as_fd(); - let attach_type = self.data.expected_attach_type.unwrap(); + let attach_type = self.data.expected_attach_type.unwrap().attach_type(); if KernelVersion::at_least(5, 7, 0) { let link_fd = bpf_link_create( prog_fd, diff --git a/aya/src/programs/fentry.rs b/aya/src/programs/fentry.rs index 0639ab2ae..246c89cf1 100644 --- a/aya/src/programs/fentry.rs +++ b/aya/src/programs/fentry.rs @@ -6,8 +6,8 @@ use aya_obj::{ }; use crate::programs::{ - FdLink, FdLinkId, ProgramData, ProgramError, ProgramType, define_link_wrapper, load_program, - utils::attach_raw_tracepoint, + ExpectedAttachType, FdLink, FdLinkId, ProgramData, ProgramError, ProgramType, + define_link_wrapper, load_program, utils::attach_raw_tracepoint, }; /// A program that can be attached to the entry point of (almost) any kernel @@ -60,7 +60,7 @@ impl FEntry { /// is entered. The `btf` argument must contain the BTF info for the /// running kernel. pub fn load(&mut self, fn_name: &str, btf: &Btf) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(BPF_TRACE_FENTRY); + self.data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_TRACE_FENTRY)); self.data.attach_btf_id = Some(btf.id_by_type_name_kind(fn_name, BtfKind::Func)?); load_program(BPF_PROG_TYPE_TRACING, &mut self.data) } diff --git a/aya/src/programs/fexit.rs b/aya/src/programs/fexit.rs index 2a4a388ed..86e55d15e 100644 --- a/aya/src/programs/fexit.rs +++ b/aya/src/programs/fexit.rs @@ -6,8 +6,8 @@ use aya_obj::{ }; use crate::programs::{ - FdLink, FdLinkId, ProgramData, ProgramError, ProgramType, define_link_wrapper, load_program, - utils::attach_raw_tracepoint, + ExpectedAttachType, FdLink, FdLinkId, ProgramData, ProgramError, ProgramType, + define_link_wrapper, load_program, utils::attach_raw_tracepoint, }; /// A program that can be attached to the exit point of (almost) anny kernel @@ -60,7 +60,7 @@ impl FExit { /// is exited. The `btf` argument must contain the BTF info for the running /// kernel. pub fn load(&mut self, fn_name: &str, btf: &Btf) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(BPF_TRACE_FEXIT); + self.data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_TRACE_FEXIT)); self.data.attach_btf_id = Some(btf.id_by_type_name_kind(fn_name, BtfKind::Func)?); load_program(BPF_PROG_TYPE_TRACING, &mut self.data) } diff --git a/aya/src/programs/flow_dissector.rs b/aya/src/programs/flow_dissector.rs index 64d657bdb..2006e17b5 100644 --- a/aya/src/programs/flow_dissector.rs +++ b/aya/src/programs/flow_dissector.rs @@ -8,8 +8,9 @@ use aya_obj::generated::{ use crate::{ programs::{ - CgroupAttachMode, FdLink, Link, ProgAttachLink, ProgramData, ProgramError, ProgramType, - define_link_wrapper, id_as_key, impl_try_into_fdlink, load_program, + CgroupAttachMode, ExpectedAttachType, FdLink, Link, ProgAttachLink, ProgramData, + ProgramError, ProgramType, define_link_wrapper, id_as_key, impl_try_into_fdlink, + load_program, }, sys::{LinkTarget, SyscallError, bpf_link_create}, util::KernelVersion, @@ -70,7 +71,7 @@ impl FlowDissector { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(BPF_FLOW_DISSECTOR); + self.data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_FLOW_DISSECTOR)); load_program(BPF_PROG_TYPE_FLOW_DISSECTOR, &mut self.data) } diff --git a/aya/src/programs/iter.rs b/aya/src/programs/iter.rs index fb0d52795..f4d2451c6 100644 --- a/aya/src/programs/iter.rs +++ b/aya/src/programs/iter.rs @@ -14,8 +14,8 @@ use aya_obj::{ use crate::{ programs::{ - FdLink, LinkError, PerfLinkIdInner, PerfLinkInner, ProgramData, ProgramError, ProgramType, - define_link_wrapper, impl_try_into_fdlink, load_program, + ExpectedAttachType, FdLink, LinkError, PerfLinkIdInner, PerfLinkInner, ProgramData, + ProgramError, ProgramType, define_link_wrapper, impl_try_into_fdlink, load_program, }, sys::{LinkTarget, SyscallError, bpf_create_iter, bpf_link_create, bpf_link_get_info_by_fd}, }; @@ -65,7 +65,7 @@ impl Iter { /// Loads the program inside the kernel. pub fn load(&mut self, iter_type: &str, btf: &Btf) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(BPF_TRACE_ITER); + self.data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_TRACE_ITER)); let type_name = format!("bpf_iter_{iter_type}"); self.data.attach_btf_id = Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?); diff --git a/aya/src/programs/links.rs b/aya/src/programs/links.rs index 1a80f9e1f..076cccc97 100644 --- a/aya/src/programs/links.rs +++ b/aya/src/programs/links.rs @@ -472,19 +472,28 @@ macro_rules! id_as_key { pub(crate) use id_as_key; -macro_rules! define_link_wrapper { - ($wrapper:ident, $wrapper_id:ident, $base:ident, $base_id:ident, $program:ident $(,)?) => { - /// The type returned by - #[doc = concat!("[`", stringify!($program), "::attach`]")] - /// . Can be passed to - #[doc = concat!("[`", stringify!($program), "::detach`]")] - /// . +/// Defines link types and conversions without adding methods to the program. +/// +/// This macro generates the link type, link ID type, and conversion implementations +/// for programs that don't have a standard `attach()` method (like StructOps where +/// the link is created via `StructOpsMap::attach()`). +/// +/// For programs with a standard `attach()` method, use [`define_link_wrapper!`] instead, +/// which calls this macro internally and also adds `detach()` and `take_link()` methods. +macro_rules! define_link_types { + ( + $(#[$wrapper_doc:meta])* + $wrapper:ident, + $(#[$wrapper_id_doc:meta])* + $wrapper_id:ident, + $base:ident, + $base_id:ident $(,)? + ) => { + $(#[$wrapper_id_doc])* #[derive(Debug, Hash, Eq, PartialEq)] pub struct $wrapper_id($base_id); - /// The link used by - #[doc = concat!("[`", stringify!($program), "`]")] - /// programs. + $(#[$wrapper_doc])* #[derive(Debug)] pub struct $wrapper(Option<$base>); @@ -540,6 +549,27 @@ macro_rules! define_link_wrapper { w.0.take().unwrap() } } + }; +} + +pub(crate) use define_link_types; + +macro_rules! define_link_wrapper { + ($wrapper:ident, $wrapper_id:ident, $base:ident, $base_id:ident, $program:ident $(,)?) => { + $crate::programs::links::define_link_types!( + /// The link used by + #[doc = concat!("[`", stringify!($program), "`]")] + /// programs. + $wrapper, + /// The type returned by + #[doc = concat!("[`", stringify!($program), "::attach`]")] + /// . Can be passed to + #[doc = concat!("[`", stringify!($program), "::detach`]")] + /// . + $wrapper_id, + $base, + $base_id, + ); impl $program { /// Detaches the program. diff --git a/aya/src/programs/lsm.rs b/aya/src/programs/lsm.rs index 776a51991..e02b56c8d 100644 --- a/aya/src/programs/lsm.rs +++ b/aya/src/programs/lsm.rs @@ -6,8 +6,8 @@ use aya_obj::{ }; use crate::programs::{ - FdLink, FdLinkId, LsmAttachType, ProgramData, ProgramError, ProgramType, define_link_wrapper, - load_program, utils::attach_raw_tracepoint, + ExpectedAttachType, FdLink, FdLinkId, LsmAttachType, ProgramData, ProgramError, ProgramType, + define_link_wrapper, load_program, utils::attach_raw_tracepoint, }; /// A program that attaches to Linux LSM hooks. Used to implement security policy and @@ -64,7 +64,7 @@ impl Lsm { /// * `lsm_hook_name` - full name of the LSM hook that the program should /// be attached to pub fn load(&mut self, lsm_hook_name: &str, btf: &Btf) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(BPF_LSM_MAC); + self.data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_LSM_MAC)); let type_name = format!("bpf_lsm_{lsm_hook_name}"); self.data.attach_btf_id = Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?); diff --git a/aya/src/programs/lsm_cgroup.rs b/aya/src/programs/lsm_cgroup.rs index 991105a1d..c70753c9d 100644 --- a/aya/src/programs/lsm_cgroup.rs +++ b/aya/src/programs/lsm_cgroup.rs @@ -9,7 +9,10 @@ use aya_obj::{ use crate::{ VerifierLogLevel, - programs::{FdLink, FdLinkId, ProgramData, ProgramError, define_link_wrapper, load_program}, + programs::{ + ExpectedAttachType, FdLink, FdLinkId, ProgramData, ProgramError, define_link_wrapper, + load_program, + }, sys::{BpfLinkCreateArgs, LinkTarget, SyscallError, bpf_link_create}, }; @@ -66,7 +69,7 @@ impl LsmCgroup { /// * `lsm_hook_name` - full name of the LSM hook that the program should /// be attached to pub fn load(&mut self, lsm_hook_name: &str, btf: &Btf) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(BPF_LSM_CGROUP); + self.data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_LSM_CGROUP)); let type_name = format!("bpf_lsm_{lsm_hook_name}"); self.data.attach_btf_id = Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?); @@ -81,7 +84,7 @@ impl LsmCgroup { /// the program being unloaded from the kernel if it is still pinned. pub fn from_pin>(path: P) -> Result { let mut data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; - data.expected_attach_type = Some(BPF_LSM_CGROUP); + data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_LSM_CGROUP)); Ok(Self { data }) } @@ -98,7 +101,7 @@ impl LsmCgroup { // - LsmCgroup::from_pin has been called // // In all cases, expected_attach_type is guaranteed to be Some(_). - let attach_type = self.data.expected_attach_type.unwrap(); + let attach_type = self.data.expected_attach_type.unwrap().attach_type(); let btf_id = self.data.attach_btf_id.ok_or(ProgramError::NotLoaded)?; let link_fd = bpf_link_create( prog_fd, diff --git a/aya/src/programs/mod.rs b/aya/src/programs/mod.rs index 9e151ad8c..d8b6d319a 100644 --- a/aya/src/programs/mod.rs +++ b/aya/src/programs/mod.rs @@ -66,6 +66,8 @@ pub mod sk_msg; pub mod sk_skb; pub mod sock_ops; pub mod socket_filter; +pub mod struct_ops; +pub mod syscall; pub mod tc; pub mod tp_btf; pub mod trace_point; @@ -119,6 +121,8 @@ pub use crate::programs::{ sk_skb::{SkSkb, SkSkbKind}, sock_ops::SockOps, socket_filter::{SocketFilter, SocketFilterError}, + struct_ops::StructOps, + syscall::Syscall, tc::{SchedClassifier, TcAttachType, TcError}, tp_btf::BtfTracePoint, trace_point::{TracePoint, TracePointError}, @@ -329,6 +333,10 @@ pub enum Program { CgroupDevice(CgroupDevice), /// An [`Iter`] program Iter(Iter), + /// A [`StructOps`] program + StructOps(StructOps), + /// A [`Syscall`] program + Syscall(Syscall), } impl Program { @@ -369,6 +377,8 @@ impl Program { Self::CgroupSock(_) => ProgramType::CgroupSock, Self::CgroupDevice(_) => ProgramType::CgroupDevice, Self::FlowDissector(_) => ProgramType::FlowDissector, + Self::StructOps(_) => ProgramType::StructOps, + Self::Syscall(_) => ProgramType::Syscall, } } @@ -402,6 +412,8 @@ impl Program { Self::CgroupSock(p) => p.pin(path), Self::CgroupDevice(p) => p.pin(path), Self::Iter(p) => p.pin(path), + Self::StructOps(p) => p.pin(path), + Self::Syscall(p) => p.pin(path), } } @@ -435,6 +447,8 @@ impl Program { Self::CgroupSock(mut p) => p.unload(), Self::CgroupDevice(mut p) => p.unload(), Self::Iter(mut p) => p.unload(), + Self::StructOps(mut p) => p.unload(), + Self::Syscall(mut p) => p.unload(), } } @@ -470,6 +484,8 @@ impl Program { Self::CgroupSock(p) => p.fd(), Self::CgroupDevice(p) => p.fd(), Self::Iter(p) => p.fd(), + Self::StructOps(p) => p.fd(), + Self::Syscall(p) => p.fd(), } } @@ -506,6 +522,48 @@ impl Program { Self::CgroupSock(p) => p.info(), Self::CgroupDevice(p) => p.info(), Self::Iter(p) => p.info(), + Self::StructOps(p) => p.info(), + Self::Syscall(p) => p.info(), + } + } +} + +/// The expected attach type for a BPF program. +/// +/// For most program types, this is a `bpf_attach_type` enum value. +/// For struct_ops programs, the kernel interprets this field as a member index +/// rather than an attach type. +#[derive(Debug, Clone, Copy)] +pub(crate) enum ExpectedAttachType { + /// Standard attach type for most program types. + AttachType(bpf_attach_type), + /// Member index for struct_ops programs. + /// + /// The kernel uses this field to identify which struct member the program + /// implements, not as a `bpf_attach_type`. + StructOpsMemberIndex(u32), +} + +impl ExpectedAttachType { + /// Returns the raw u32 value to pass to the kernel. + pub(crate) fn as_raw(&self) -> u32 { + match self { + Self::AttachType(t) => *t as u32, + Self::StructOpsMemberIndex(idx) => *idx, + } + } + + /// Returns the `bpf_attach_type` if this is an `AttachType` variant. + /// + /// # Panics + /// + /// Panics if this is a `StructOpsMemberIndex` variant. + pub(crate) fn attach_type(&self) -> bpf_attach_type { + match self { + Self::AttachType(t) => *t, + Self::StructOpsMemberIndex(_) => { + panic!("expected AttachType variant, got StructOpsMemberIndex") + } } } } @@ -516,7 +574,7 @@ pub(crate) struct ProgramData { pub(crate) obj: Option<(aya_obj::Program, aya_obj::Function)>, pub(crate) fd: Option, pub(crate) links: Links, - pub(crate) expected_attach_type: Option, + pub(crate) expected_attach_type: Option, pub(crate) attach_btf_obj_fd: Option, pub(crate) attach_btf_id: Option, pub(crate) attach_prog_fd: Option, @@ -702,7 +760,7 @@ fn load_program( insns: instructions, license, kernel_version: target_kernel_version, - expected_attach_type: *expected_attach_type, + expected_attach_type: expected_attach_type.map(|t| t.as_raw()), prog_btf_fd: btf_fd.as_ref().map(|f| f.as_fd()), attach_btf_obj_fd: attach_btf_obj_fd.as_ref().map(|fd| fd.as_fd()), attach_btf_id: *attach_btf_id, @@ -823,6 +881,8 @@ impl_program_unload!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); macro_rules! impl_fd { @@ -866,6 +926,8 @@ impl_fd!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); /// Trait implemented by the [`Program`] types which support the kernel's @@ -974,6 +1036,8 @@ impl_program_pin!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); macro_rules! impl_from_pin { @@ -1016,6 +1080,25 @@ impl_from_pin!( Iter, ); +// StructOps needs special handling for from_pin since it has member_name field +impl StructOps { + /// 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`]. + /// + /// On drop, any managed links are detached and the program is unloaded. This will not result in + /// the program being unloaded from the kernel if it is still pinned. + /// + /// Note: The member name cannot be determined from a pinned program. + pub fn from_pin>(path: P) -> Result { + let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; + Ok(Self { + data, + member_name: String::new(), + }) + } +} + macro_rules! impl_from_prog_info { ( $(#[$doc:meta])* @@ -1180,6 +1263,8 @@ impl_try_from_program!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); impl_info!( @@ -1210,6 +1295,8 @@ impl_info!( CgroupSock, CgroupDevice, Iter, + StructOps, + Syscall, ); /// Returns an iterator over all loaded links. diff --git a/aya/src/programs/sk_lookup.rs b/aya/src/programs/sk_lookup.rs index 21577583e..c41fe28c2 100644 --- a/aya/src/programs/sk_lookup.rs +++ b/aya/src/programs/sk_lookup.rs @@ -6,7 +6,8 @@ use aya_obj::generated::{bpf_attach_type::BPF_SK_LOOKUP, bpf_prog_type::BPF_PROG use super::links::FdLink; use crate::{ programs::{ - FdLinkId, ProgramData, ProgramError, ProgramType, define_link_wrapper, load_program, + ExpectedAttachType, FdLinkId, ProgramData, ProgramError, ProgramType, define_link_wrapper, + load_program, }, sys::{LinkTarget, SyscallError, bpf_link_create}, }; @@ -59,7 +60,7 @@ impl SkLookup { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(BPF_SK_LOOKUP); + self.data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_SK_LOOKUP)); load_program(BPF_PROG_TYPE_SK_LOOKUP, &mut self.data) } diff --git a/aya/src/programs/struct_ops.rs b/aya/src/programs/struct_ops.rs new file mode 100644 index 000000000..74db07e03 --- /dev/null +++ b/aya/src/programs/struct_ops.rs @@ -0,0 +1,101 @@ +//! struct_ops programs. + +use aya_obj::{ + btf::{Btf, BtfKind}, + generated::bpf_prog_type::BPF_PROG_TYPE_STRUCT_OPS, +}; +use log::debug; + +use crate::programs::{ + ExpectedAttachType, FdLink, FdLinkId, ProgramData, ProgramError, ProgramType, load_program, +}; + +/// A program that implements a kernel struct_ops interface. +/// +/// Struct ops programs are used to implement kernel interfaces like `sched_ext_ops` for +/// custom schedulers or `hid_bpf_ops` for HID device handling. Unlike other BPF program +/// types, struct_ops programs are callbacks that the kernel invokes when specific +/// operations are needed. +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.6. +/// +/// # Examples +/// +/// ```no_run +/// # #[derive(thiserror::Error, Debug)] +/// # enum Error { +/// # #[error(transparent)] +/// # BtfError(#[from] aya::BtfError), +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Ebpf(#[from] aya::EbpfError), +/// # } +/// # let mut bpf = Ebpf::load_file("ebpf_programs.o")?; +/// use aya::{Ebpf, programs::StructOps, Btf}; +/// +/// let btf = Btf::from_sys_fs()?; +/// let program: &mut StructOps = bpf.program_mut("my_struct_ops").unwrap().try_into()?; +/// program.load("hid_bpf_ops", &btf)?; +/// # Ok::<(), Error>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_STRUCT_OPS")] +pub struct StructOps { + pub(crate) data: ProgramData, + /// The struct member name this program implements (from section name) + pub(crate) member_name: String, +} + +impl StructOps { + /// The type of the program according to the kernel. + pub const PROGRAM_TYPE: ProgramType = ProgramType::StructOps; + + /// Loads the program inside the kernel. + /// + /// # Arguments + /// + /// * `struct_name` - the name of the struct_ops type (e.g., "hid_bpf_ops", + /// "sched_ext_ops") + /// * `btf` - the BTF information for the running kernel + /// + /// The member name is automatically determined from the program's section name + /// (e.g., `struct_ops/hid_device_event` -> `hid_device_event`). + pub fn load(&mut self, struct_name: &str, btf: &Btf) -> Result<(), ProgramError> { + let struct_type_id = btf.id_by_type_name_kind(struct_name, BtfKind::Struct)?; + let member_index = btf.struct_member_index(struct_type_id, &self.member_name)?; + + debug!( + "loading struct_ops program member='{}' struct='{struct_name}' member_index={member_index}", + self.member_name + ); + + // For struct_ops, expected_attach_type stores the member index (not a bpf_attach_type). + // The kernel interprets this field as a raw u32 for struct_ops programs. + self.data.expected_attach_type = + Some(ExpectedAttachType::StructOpsMemberIndex(member_index)); + self.data.attach_btf_id = Some(struct_type_id); + load_program(BPF_PROG_TYPE_STRUCT_OPS, &mut self.data) + } + + /// Returns the struct member name this program implements. + pub fn member_name(&self) -> &str { + &self.member_name + } +} + +crate::programs::links::define_link_types!( + /// The link used by [`StructOps`] programs. + /// + /// This is created by [`StructOpsMap::attach`](crate::maps::StructOpsMap::attach) + /// after the struct_ops map has been registered. + StructOpsLink, + /// The identifier for a [`StructOpsLink`]. + /// + /// This is returned by [`StructOpsMap::attach`](crate::maps::StructOpsMap::attach). + StructOpsLinkId, + FdLink, + FdLinkId, +); diff --git a/aya/src/programs/syscall.rs b/aya/src/programs/syscall.rs new file mode 100644 index 000000000..e0c054bd1 --- /dev/null +++ b/aya/src/programs/syscall.rs @@ -0,0 +1,82 @@ +//! Syscall programs. +//! +//! BPF_PROG_TYPE_SYSCALL programs can be invoked directly via the bpf() syscall. +//! They are used for various purposes including HID-BPF probe functions. + +use aya_obj::generated::bpf_prog_type::BPF_PROG_TYPE_SYSCALL; + +use crate::programs::{ProgramData, ProgramError, ProgramType, load_program}; + +/// A BPF program that can be invoked directly via the bpf() syscall. +/// +/// Syscall programs are typically used for: +/// - HID-BPF probe functions that determine whether to attach to a device +/// - General-purpose programs invoked via BPF_PROG_RUN +/// +/// # Minimum kernel version +/// +/// The minimum kernel version required to use this feature is 5.14. +/// +/// # Examples +/// +/// ```no_run +/// # #[derive(Debug, thiserror::Error)] +/// # enum Error { +/// # #[error(transparent)] +/// # Program(#[from] aya::programs::ProgramError), +/// # #[error(transparent)] +/// # Ebpf(#[from] aya::EbpfError) +/// # } +/// # let mut bpf = aya::Ebpf::load(&[])?; +/// use aya::programs::Syscall; +/// +/// let prog: &mut Syscall = bpf.program_mut("probe").unwrap().try_into()?; +/// prog.load()?; +/// // The program can now be invoked via bpf(BPF_PROG_RUN, ...) +/// // For HID-BPF, the kernel invokes the probe automatically during struct_ops attachment +/// # Ok::<(), Error>(()) +/// ``` +#[derive(Debug)] +#[doc(alias = "BPF_PROG_TYPE_SYSCALL")] +pub struct Syscall { + pub(crate) data: ProgramData, +} + +impl Syscall { + /// The type of the program according to the kernel. + pub const PROGRAM_TYPE: ProgramType = ProgramType::Syscall; + + /// Loads the program inside the kernel. + pub fn load(&mut self) -> Result<(), ProgramError> { + load_program(BPF_PROG_TYPE_SYSCALL, &mut self.data) + } +} + +/// A link for syscall programs. +/// +/// Syscall programs don't attach to anything in the traditional sense, +/// they are invoked directly. This is a placeholder for API consistency. +#[derive(Debug)] +pub struct SyscallLink { + _private: (), +} + +/// The type returned when detaching syscall links. Syscall programs don't +/// actually attach, so this is a no-op placeholder. +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct SyscallLinkId(()); + +impl crate::programs::Link for SyscallLink { + type Id = SyscallLinkId; + + fn id(&self) -> Self::Id { + SyscallLinkId(()) + } + + fn detach(self) -> Result<(), ProgramError> { + // Syscall programs don't attach to anything + Ok(()) + } +} + +crate::programs::id_as_key!(SyscallLink, SyscallLinkId); diff --git a/aya/src/programs/tp_btf.rs b/aya/src/programs/tp_btf.rs index 873405c3a..d6850cf88 100644 --- a/aya/src/programs/tp_btf.rs +++ b/aya/src/programs/tp_btf.rs @@ -6,8 +6,8 @@ use aya_obj::{ }; use crate::programs::{ - FdLink, FdLinkId, ProgramData, ProgramError, ProgramType, define_link_wrapper, load_program, - utils::attach_raw_tracepoint, + ExpectedAttachType, FdLink, FdLinkId, ProgramData, ProgramError, ProgramType, + define_link_wrapper, load_program, utils::attach_raw_tracepoint, }; /// Marks a function as a [BTF-enabled raw tracepoint][1] eBPF program that can be attached at @@ -62,7 +62,7 @@ impl BtfTracePoint { /// * `tracepoint` - full name of the tracepoint that we should attach to /// * `btf` - btf information for the target system pub fn load(&mut self, tracepoint: &str, btf: &Btf) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(BPF_TRACE_RAW_TP); + self.data.expected_attach_type = Some(ExpectedAttachType::AttachType(BPF_TRACE_RAW_TP)); let type_name = format!("btf_trace_{tracepoint}"); self.data.attach_btf_id = Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Typedef)?); diff --git a/aya/src/programs/xdp.rs b/aya/src/programs/xdp.rs index 8ba628f9d..3f6deb6e1 100644 --- a/aya/src/programs/xdp.rs +++ b/aya/src/programs/xdp.rs @@ -19,8 +19,8 @@ use thiserror::Error; use crate::{ VerifierLogLevel, programs::{ - FdLink, Link, LinkError, ProgramData, ProgramError, ProgramType, define_link_wrapper, - id_as_key, impl_try_into_fdlink, load_program, + ExpectedAttachType, FdLink, Link, LinkError, ProgramData, ProgramError, ProgramType, + define_link_wrapper, id_as_key, impl_try_into_fdlink, load_program, }, sys::{ LinkTarget, NetlinkError, SyscallError, bpf_link_create, bpf_link_get_info_by_fd, @@ -88,7 +88,8 @@ impl Xdp { /// Loads the program inside the kernel. pub fn load(&mut self) -> Result<(), ProgramError> { - self.data.expected_attach_type = Some(self.attach_type.into()); + self.data.expected_attach_type = + Some(ExpectedAttachType::AttachType(self.attach_type.into())); load_program(bpf_prog_type::BPF_PROG_TYPE_XDP, &mut self.data) } @@ -138,7 +139,7 @@ impl Xdp { // instance through `Xdp:try_from(Program)` does not set any fd. // So, in all cases where we have an fd, we have an expected_attach_type. Thus, if we // reach this point, expected_attach_type is guaranteed to be Some(_). - let attach_type = self.data.expected_attach_type.unwrap(); + let attach_type = self.data.expected_attach_type.unwrap().attach_type(); let link = match bpf_link_create( prog_fd, LinkTarget::IfIndex(if_index), @@ -183,7 +184,7 @@ impl Xdp { attach_type: XdpAttachType, ) -> Result { let mut data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?; - data.expected_attach_type = Some(attach_type.into()); + data.expected_attach_type = Some(ExpectedAttachType::AttachType(attach_type.into())); Ok(Self { data, attach_type }) } diff --git a/aya/src/sys/bpf.rs b/aya/src/sys/bpf.rs index 7a6a0ef28..0f633364c 100644 --- a/aya/src/sys/bpf.rs +++ b/aya/src/sys/bpf.rs @@ -27,7 +27,7 @@ use libc::{ EBADF, ENOENT, ENOSPC, EPERM, RLIM_INFINITY, RLIMIT_MEMLOCK, getrlimit, rlim_t, rlimit, setrlimit, }; -use log::warn; +use log::{debug, warn}; use crate::{ Btf, Pod, VerifierLogLevel, @@ -51,6 +51,15 @@ pub(crate) fn bpf_create_map( name: &CStr, def: &aya_obj::Map, btf_fd: Option>, +) -> io::Result { + bpf_create_map_with_vmlinux_btf(name, def, btf_fd, None) +} + +pub(crate) fn bpf_create_map_with_vmlinux_btf( + name: &CStr, + def: &aya_obj::Map, + btf_fd: Option>, + btf_vmlinux_value_type_id: Option, ) -> io::Result { let mut attr = unsafe { mem::zeroed::() }; @@ -61,7 +70,18 @@ pub(crate) fn bpf_create_map( u.max_entries = def.max_entries(); u.map_flags = def.map_flags(); - if let aya_obj::Map::Btf(m) = def { + // Handle struct_ops maps - they use btf_vmlinux_value_type_id exclusively. + // When btf_vmlinux_value_type_id is set, btf_value_type_id MUST be 0 (kernel rejects otherwise). + // See kernel/bpf/syscall.c map_create() validation. + // However, btf_fd IS required to resolve struct/function types in the BPF object's BTF. + if let aya_obj::Map::StructOps(_) = def { + if let Some(vmlinux_type_id) = btf_vmlinux_value_type_id { + u.btf_vmlinux_value_type_id = vmlinux_type_id; + } + // btf_value_type_id must be 0 (already zeroed via mem::zeroed) + // btf_fd IS needed for struct_ops to resolve struct/function references + u.btf_fd = btf_fd.map(|fd| fd.as_raw_fd()).unwrap_or_default() as u32; + } else if let aya_obj::Map::Btf(m) = def { use bpf_map_type::*; // Mimic https://github.com/libbpf/libbpf/issues/355 @@ -130,7 +150,11 @@ pub(crate) struct EbpfLoadProgramAttrs<'a> { pub(crate) insns: &'a [bpf_insn], pub(crate) license: &'a CStr, pub(crate) kernel_version: u32, - pub(crate) expected_attach_type: Option, + /// The expected attach type as a raw u32 value. + /// + /// For most program types, this is a `bpf_attach_type` cast to u32. + /// For struct_ops, this is the struct member index. + pub(crate) expected_attach_type: Option, pub(crate) prog_btf_fd: Option>, pub(crate) attach_btf_obj_fd: Option>, pub(crate) attach_btf_id: Option, @@ -161,10 +185,17 @@ pub(crate) fn bpf_load_program( u.prog_flags = aya_attr.flags; u.prog_type = aya_attr.ty as u32; if let Some(v) = aya_attr.expected_attach_type { - u.expected_attach_type = v as u32; + u.expected_attach_type = v; } u.insns = aya_attr.insns.as_ptr() as u64; u.insn_cnt = aya_attr.insns.len() as u32; + debug!( + "bpf_load_program: prog_type={} insn_cnt={} expected_attach_type={:?} attach_btf_id={:?}", + u.prog_type, + aya_attr.insns.len(), + aya_attr.expected_attach_type, + aya_attr.attach_btf_id + ); u.license = aya_attr.license.as_ptr() as u64; u.kern_version = aya_attr.kernel_version; @@ -454,6 +485,33 @@ pub(crate) fn bpf_link_create( unsafe { fd_sys_bpf(bpf_cmd::BPF_LINK_CREATE, &mut attr) } } +/// Creates a BPF link for a struct_ops map. +/// +/// For link-based struct_ops (maps created with BPF_F_LINK flag), +/// this activates the struct_ops after bpf_map_update_elem has been called. +/// +/// # Arguments +/// +/// * `map_fd` - The file descriptor of the struct_ops map +/// +/// # Returns +/// +/// A new file descriptor for the BPF link on success. +// since kernel 5.13 (for struct_ops links) +pub(crate) fn bpf_struct_ops_link_create(map_fd: BorrowedFd<'_>) -> io::Result { + let mut attr = unsafe { mem::zeroed::() }; + + // For struct_ops, the kernel reuses prog_fd field for the map_fd + attr.link_create.__bindgen_anon_1.prog_fd = map_fd.as_raw_fd() as u32; + // target_fd is 0 for struct_ops + attr.link_create.__bindgen_anon_2.target_fd = 0; + // BPF_STRUCT_OPS = 44 + attr.link_create.attach_type = bpf_attach_type::BPF_STRUCT_OPS as u32; + + // SAFETY: BPF_LINK_CREATE returns a new file descriptor. + unsafe { fd_sys_bpf(bpf_cmd::BPF_LINK_CREATE, &mut attr) } +} + // since kernel 5.7 pub(crate) fn bpf_link_update( link_fd: BorrowedFd<'_>, diff --git a/ebpf/aya-ebpf/src/helpers.rs b/ebpf/aya-ebpf/src/helpers.rs index 44b613425..eb0a4f34d 100644 --- a/ebpf/aya-ebpf/src/helpers.rs +++ b/ebpf/aya-ebpf/src/helpers.rs @@ -703,15 +703,21 @@ macro_rules! bpf_printk { pub use bpf_printk; /// Argument ready to be passed to `printk` BPF helper. +/// +/// This wraps a `u64` directly (not `[u8; 8]`) to ensure correct ABI handling +/// when passed as a variadic argument to `bpf_trace_printk`. The C ABI for +/// variadic functions may handle arrays differently than scalar types, causing +/// incorrect values to be printed. Using `u64` ensures the value is passed +/// by value in a register. #[repr(transparent)] #[derive(Copy, Clone)] -pub struct PrintkArg([u8; 8]); +pub struct PrintkArg(u64); impl PrintkArg { /// Manually construct a `printk` BPF helper argument. #[inline] pub fn from_raw(x: u64) -> Self { - Self(x.to_ne_bytes()) + Self(x) } } @@ -720,9 +726,9 @@ macro_rules! impl_integer_promotion { /// Create `printk` arguments from integer types. impl From<$ty> for PrintkArg { #[inline] - #[allow(trivial_numeric_casts)] + #[allow(trivial_numeric_casts, clippy::cast_sign_loss)] fn from(x: $ty) -> Self { - Self((x as $via).to_ne_bytes()) + Self(x as $via as u64) } } )*} @@ -746,7 +752,7 @@ impl_integer_promotion!( impl From<*const T> for PrintkArg { #[inline] fn from(x: *const T) -> Self { - Self((x as usize).to_ne_bytes()) + Self(x as usize as u64) } } @@ -754,7 +760,7 @@ impl From<*const T> for PrintkArg { impl From<*mut T> for PrintkArg { #[inline] fn from(x: *mut T) -> Self { - Self((x as usize).to_ne_bytes()) + Self(x as usize as u64) } } diff --git a/ebpf/aya-ebpf/src/programs/hid_bpf.rs b/ebpf/aya-ebpf/src/programs/hid_bpf.rs new file mode 100644 index 000000000..6ac62715b --- /dev/null +++ b/ebpf/aya-ebpf/src/programs/hid_bpf.rs @@ -0,0 +1,570 @@ +//! HID-BPF program support. +//! +//! HID-BPF is a struct_ops-based interface for implementing HID device drivers +//! in BPF. It allows intercepting and modifying HID reports, fixing report +//! descriptors, and handling hardware requests. +//! +//! # Safe Abstractions +//! +//! This module provides safe Rust abstractions over the raw kernel interfaces: +//! +//! - [`HidBpfContext`]: Safe wrapper around kernel's `hid_bpf_ctx` +//! - [`HidBpfData`]: Bounds-checked access to HID report data buffers +//! - `AllocatedContext`: RAII guard for allocated contexts (auto-releases on drop, BPF target only) +//! +//! # Callbacks +//! +//! HID-BPF supports several callbacks: +//! +//! - `hid_device_event`: Called for each HID input report +//! - `hid_rdesc_fixup`: Called to modify the HID report descriptor at probe time +//! - `hid_hw_request`: Called for hardware requests (feature reports, etc.) +//! - `hid_hw_output_report`: Called for output reports +//! +//! # Example +//! +//! ```ignore +//! use aya_ebpf::programs::hid_bpf::{HidBpfContext, HidReportType, HidClassRequest}; +//! +//! #[no_mangle] +//! #[link_section = "struct_ops/hid_device_event"] +//! pub extern "C" fn my_hid_event(ctx_ptr: *mut hid_bpf_ctx) -> i32 { +//! let ctx = HidBpfContext::new(ctx_ptr); +//! +//! // Safe bounds-checked data access +//! let Some(data) = ctx.data(0, 8) else { +//! return 0; +//! }; +//! +//! // Read bytes safely +//! if let Some(first_byte) = data.get(0) { +//! // Process... +//! } +//! +//! 0 +//! } +//! ``` + +use core::{ffi::c_void, marker::PhantomData}; + +use crate::EbpfContext; + +// ============================================================================= +// Kernel type definitions +// ============================================================================= + +/// Kernel's HID-BPF context structure. +/// +/// This struct must be named `hid_bpf_ctx` (snake_case) to match the kernel's +/// BTF type name. The kernel verifier checks that kfunc arguments point to +/// this exact struct type. +/// +/// # Layout +/// +/// This matches the kernel's `struct hid_bpf_ctx` from `include/linux/hid_bpf.h`. +/// +/// # Note on struct naming +/// +/// This struct is intentionally NOT named `hid_bpf_ctx` to avoid BTF collision +/// with the kernel's vmlinux type. When the verifier checks kfunc arguments, +/// it resolves types by name from vmlinux BTF, finding the kernel's definition +/// which has `struct hid_device *hid` (a pointer to struct, not scalar). +/// +/// By using a different name, we ensure the verifier uses our scalar-field +/// definition for type checking, avoiding the "must point to scalar" error. +/// +/// The actual kernel ABI is preserved since we only access fields by offset +/// through the context pointer, and kfunc calls use inline asm which bypasses +/// BTF type matching. +#[repr(C)] +pub struct hid_bpf_ctx { + /// Pointer to the HID device stored as opaque u64 (do not use directly). + /// The kernel passes an actual pointer here, but we must represent it as + /// a scalar integer to satisfy the struct_ops verifier. + pub hid: u64, + /// Allocated size for data buffer access. + pub allocated_size: u32, + /// Return value (same memory as size in kernel's union). + pub retval: i32, +} + +// ============================================================================= +// Safe data buffer wrapper +// ============================================================================= + +/// Safe wrapper around HID report data buffer with bounds checking. +/// +/// This type provides safe indexed access to the kernel's HID report buffer, +/// preventing out-of-bounds access that would be undefined behavior. +/// +/// # Lifetime +/// +/// The lifetime `'a` is tied to the [`HidBpfContext`] that created this buffer, +/// ensuring the buffer cannot outlive the context. +pub struct HidBpfData<'a> { + ptr: *mut u8, + len: u32, + _marker: PhantomData<&'a mut [u8]>, +} + +impl HidBpfData<'_> { + /// Get a byte at the given index. + /// + /// Returns `None` if the index is out of bounds. + #[inline(always)] + pub fn get(&self, idx: usize) -> Option { + if idx < self.len as usize { + // SAFETY: bounds checked above, ptr valid for len bytes per kernel contract + Some(unsafe { *self.ptr.add(idx) }) + } else { + None + } + } + + /// Set a byte at the given index. + /// + /// Returns `true` if successful, `false` if out of bounds. + #[inline(always)] + pub fn set(&mut self, idx: usize, val: u8) -> bool { + if idx < self.len as usize { + // SAFETY: bounds checked above, ptr valid for len bytes per kernel contract + unsafe { *self.ptr.add(idx) = val }; + true + } else { + false + } + } + + /// Copy a slice into the buffer at the given offset. + /// + /// Returns `true` if successful, `false` if the write would exceed bounds. + #[inline(always)] + pub fn copy_from_slice(&mut self, offset: usize, src: &[u8]) -> bool { + let end = offset.saturating_add(src.len()); + if end > self.len as usize { + return false; + } + for (i, &byte) in src.iter().enumerate() { + // SAFETY: bounds checked above + unsafe { *self.ptr.add(offset + i) = byte }; + } + true + } + + /// Check if the buffer starts with the given pattern. + #[inline(always)] + pub fn starts_with(&self, pattern: &[u8]) -> bool { + if pattern.len() > self.len as usize { + return false; + } + for (i, &expected) in pattern.iter().enumerate() { + // SAFETY: bounds checked above + if unsafe { *self.ptr.add(i) } != expected { + return false; + } + } + true + } + + /// Returns the length of the buffer. + #[inline(always)] + pub fn len(&self) -> usize { + self.len as usize + } + + /// Returns true if the buffer is empty. + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.len == 0 + } +} + +// ============================================================================= +// Context wrapper +// ============================================================================= + +/// Safe wrapper around the kernel's `hid_bpf_ctx`. +/// +/// This wrapper provides safe access to context fields and data buffers. +/// All unsafe operations are encapsulated within this type's methods. +#[repr(transparent)] +pub struct HidBpfContext { + ctx: *mut hid_bpf_ctx, +} + +impl HidBpfContext { + /// Creates a new HidBpfContext from a raw `hid_bpf_ctx` pointer. + /// + /// # Safety + /// + /// The pointer must be a valid `struct hid_bpf_ctx *` from the kernel. + /// This is guaranteed when called from a HID-BPF callback. + #[inline(always)] + pub unsafe fn new(ctx: *mut hid_bpf_ctx) -> Self { + Self { ctx } + } + + /// Get safe access to the HID report data buffer. + /// + /// Returns `None` if the kernel returns a null pointer (e.g., invalid offset/size). + #[cfg(target_arch = "bpf")] + #[inline(always)] + pub fn data(&self, offset: u32, size: u32) -> Option> { + // SAFETY: kfunc returns valid pointer or null, size is validated by kernel + let ptr = unsafe { kfunc::hid_bpf_get_data(self.ctx, offset, size) }; + if ptr.is_null() { + None + } else { + Some(HidBpfData { + ptr, + len: size, + _marker: PhantomData, + }) + } + } + + /// Returns the allocated buffer size from the context. + #[inline(always)] + pub fn allocated_size(&self) -> u32 { + // SAFETY: context pointer valid per kernel contract + unsafe { (*self.ctx).allocated_size } + } + + /// Returns the retval field from the context. + /// + /// For `hid_rdesc_fixup`, this contains the original descriptor size. + #[inline(always)] + pub fn retval(&self) -> i32 { + // SAFETY: context pointer valid per kernel contract + unsafe { (*self.ctx).retval } + } + + /// Sets the retval field in the context. + #[inline(always)] + pub fn set_retval(&mut self, val: i32) { + // SAFETY: context pointer valid per kernel contract + unsafe { (*self.ctx).retval = val }; + } + + /// Returns a raw pointer to the underlying `hid_bpf_ctx`. + /// + /// Use this when you need to pass the context to kfuncs directly. + #[inline(always)] + pub fn as_ptr(&self) -> *mut hid_bpf_ctx { + self.ctx + } +} + +impl EbpfContext for HidBpfContext { + fn as_ptr(&self) -> *mut c_void { + self.ctx.cast::() + } +} + +// ============================================================================= +// RAII guard for allocated contexts +// ============================================================================= + +/// RAII guard for an allocated HID-BPF context. +/// +/// When you need to send requests to a different HID interface (e.g., sending +/// commands to a vendor interface while handling events on the keyboard interface), +/// you allocate a new context. This guard ensures the context is properly released +/// when dropped, even on early returns or panics. +/// +/// # Example +/// +/// ```ignore +/// // Allocate context for vendor interface +/// let Some(vendor) = AllocatedContext::new(vendor_hid_id) else { +/// return 0; +/// }; +/// +/// // Send command - context auto-released on drop +/// let ret = vendor.hw_request(&mut buf, HidReportType::Feature, HidClassRequest::SetReport); +/// ``` +#[cfg(target_arch = "bpf")] +pub struct AllocatedContext { + ctx: *mut hid_bpf_ctx, +} + +#[cfg(target_arch = "bpf")] +impl AllocatedContext { + /// Allocate a new HID-BPF context for the given HID ID. + /// + /// Returns `None` if allocation fails (e.g., invalid HID ID). + #[inline(always)] + pub fn new(hid_id: u32) -> Option { + // SAFETY: kfunc returns valid pointer or null + let ctx = unsafe { kfunc::hid_bpf_allocate_context(hid_id) }; + if ctx.is_null() { + None + } else { + Some(Self { ctx }) + } + } + + /// Send a HID hardware request through this context. + /// + /// # Arguments + /// + /// * `buf` - Buffer for request data (and response for GET requests) + /// * `rtype` - Report type (Input, Output, or Feature) + /// * `reqtype` - Class request type (GetReport, SetReport, etc.) + /// + /// # Returns + /// + /// Number of bytes transferred on success, negative error code on failure. + #[inline(always)] + pub fn hw_request( + &self, + buf: &mut [u8], + rtype: HidReportType, + reqtype: HidClassRequest, + ) -> i32 { + // SAFETY: context valid, buffer valid for its length + unsafe { + kfunc::hid_bpf_hw_request( + self.ctx, + buf.as_mut_ptr(), + buf.len(), + rtype as u32, + reqtype as u32, + ) + } + } + + /// Returns a raw pointer to the underlying context. + #[inline(always)] + pub fn as_ptr(&self) -> *mut hid_bpf_ctx { + self.ctx + } +} + +#[cfg(target_arch = "bpf")] +impl Drop for AllocatedContext { + #[inline(always)] + fn drop(&mut self) { + // SAFETY: context was allocated successfully in new() + unsafe { kfunc::hid_bpf_release_context(self.ctx) }; + } +} + +// ============================================================================= +// Kernel function (kfunc) bindings +// ============================================================================= + +/// Low-level kernel function bindings for HID-BPF. +/// +/// These functions call kernel kfuncs using inline assembly to ensure reliable +/// invocation. The extern declarations are for BTF generation only. +/// +/// Most users should use the safe wrappers ([`HidBpfContext`], [`AllocatedContext`]) +/// instead of calling these directly. +#[cfg(target_arch = "bpf")] +pub mod kfunc { + use super::hid_bpf_ctx; + + // External kfunc declarations with proper type signatures. + // Using *mut hid_bpf_ctx to match our struct definition. + #[allow(improper_ctypes)] + unsafe extern "C" { + #[link_name = "hid_bpf_get_data"] + fn extern_hid_bpf_get_data(ctx: *mut hid_bpf_ctx, offset: u32, size: u32) -> *mut u8; + #[link_name = "hid_bpf_allocate_context"] + fn extern_hid_bpf_allocate_context(hid_id: u32) -> *mut hid_bpf_ctx; + #[link_name = "hid_bpf_release_context"] + fn extern_hid_bpf_release_context(ctx: *mut hid_bpf_ctx); + #[link_name = "hid_bpf_hw_request"] + fn extern_hid_bpf_hw_request( + ctx: *mut hid_bpf_ctx, + buf: *mut u8, + buf_sz: usize, + rtype: u32, + reqtype: u32, + ) -> i32; + } + + // Force externs to be emitted in .ksyms section for BTF generation. + // Using typed function pointers with *mut hid_bpf_ctx. + #[used] + #[unsafe(link_section = ".ksyms")] + static HID_BPF_GET_DATA_REF: unsafe extern "C" fn(*mut hid_bpf_ctx, u32, u32) -> *mut u8 = + extern_hid_bpf_get_data; + + #[used] + #[unsafe(link_section = ".ksyms")] + static HID_BPF_ALLOCATE_CONTEXT_REF: unsafe extern "C" fn(u32) -> *mut hid_bpf_ctx = + extern_hid_bpf_allocate_context; + + #[used] + #[unsafe(link_section = ".ksyms")] + static HID_BPF_RELEASE_CONTEXT_REF: unsafe extern "C" fn(*mut hid_bpf_ctx) = + extern_hid_bpf_release_context; + + #[used] + #[unsafe(link_section = ".ksyms")] + static HID_BPF_HW_REQUEST_REF: unsafe extern "C" fn( + *mut hid_bpf_ctx, + *mut u8, + usize, + u32, + u32, + ) -> i32 = extern_hid_bpf_hw_request; + + /// Get a pointer to the HID report data buffer. + /// + /// # Safety + /// + /// - `ctx` must be a valid `hid_bpf_ctx` pointer from a HID-BPF callback + /// - The returned pointer (if non-null) is valid for `size` bytes + #[inline(always)] + pub unsafe fn hid_bpf_get_data(ctx: *mut hid_bpf_ctx, offset: u32, size: u32) -> *mut u8 { + let result: *mut u8; + unsafe { + core::arch::asm!( + "call hid_bpf_get_data", + in("r1") ctx, + in("r2") offset, + in("r3") size, + lateout("r0") result, + clobber_abi("C"), + ); + } + result + } + + /// Allocate a new HID-BPF context for the given HID ID. + /// + /// # Safety + /// + /// - Must be called from a sleepable BPF context + /// - The returned context must be released with [`hid_bpf_release_context`] + #[inline(always)] + pub unsafe fn hid_bpf_allocate_context(hid_id: u32) -> *mut hid_bpf_ctx { + let result: *mut hid_bpf_ctx; + unsafe { + core::arch::asm!( + "call hid_bpf_allocate_context", + in("r1") hid_id, + lateout("r0") result, + clobber_abi("C"), + ); + } + result + } + + /// Release an allocated HID-BPF context. + /// + /// # Safety + /// + /// - `ctx` must have been allocated by [`hid_bpf_allocate_context`] + /// - Must not be called twice on the same context + #[inline(always)] + pub unsafe fn hid_bpf_release_context(ctx: *mut hid_bpf_ctx) { + unsafe { + core::arch::asm!( + "call hid_bpf_release_context", + in("r1") ctx, + clobber_abi("C"), + ); + } + } + + /// Send a HID hardware request. + /// + /// # Safety + /// + /// - `ctx` must be a valid allocated context + /// - `buf` must be valid for `buf_sz` bytes + #[inline(always)] + pub unsafe fn hid_bpf_hw_request( + ctx: *mut hid_bpf_ctx, + buf: *mut u8, + buf_sz: usize, + rtype: u32, + reqtype: u32, + ) -> i32 { + let result: i32; + unsafe { + core::arch::asm!( + "call hid_bpf_hw_request", + in("r1") ctx, + in("r2") buf, + in("r3") buf_sz, + in("r4") rtype, + in("r5") reqtype, + lateout("r0") result, + clobber_abi("C"), + ); + } + result + } +} + +// ============================================================================= +// Enums and constants +// ============================================================================= + +/// HID report types. +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HidReportType { + /// Input report - data from device to host. + Input = 0, + /// Output report - data from host to device. + Output = 1, + /// Feature report - bidirectional configuration data. + Feature = 2, +} + +/// HID class request types. +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HidClassRequest { + /// Get report request. + GetReport = 0x01, + /// Get idle request. + GetIdle = 0x02, + /// Get protocol request. + GetProtocol = 0x03, + /// Set report request. + SetReport = 0x09, + /// Set idle request. + SetIdle = 0x0a, + /// Set protocol request. + SetProtocol = 0x0b, +} + +/// Arguments for HID-BPF probe syscall. +/// +/// This struct is passed to the probe function to determine if the BPF program +/// should attach to a specific HID device interface. +#[repr(C)] +pub struct HidBpfProbeArgs { + /// HID device ID. + pub hid: u32, + /// Size of the report descriptor. + pub rdesc_size: u32, + /// The raw report descriptor bytes. + pub rdesc: [u8; 4096], + /// Return value - set to 0 to attach, negative errno to skip. + pub retval: i32, +} + +// Bus types from linux/input.h +/// USB bus type. +pub const BUS_USB: u16 = 0x03; +/// Bluetooth bus type. +pub const BUS_BLUETOOTH: u16 = 0x05; +/// I2C bus type. +pub const BUS_I2C: u16 = 0x18; + +// HID groups from linux/hid.h +/// Match any HID group. +pub const HID_GROUP_ANY: u16 = 0x0000; +/// Generic HID group. +pub const HID_GROUP_GENERIC: u16 = 0x0001; + +/// Return value to indicate the event should be ignored. +pub const HID_IGNORE_EVENT: i32 = -1; diff --git a/ebpf/aya-ebpf/src/programs/mod.rs b/ebpf/aya-ebpf/src/programs/mod.rs index f95b38dee..1cbe645e7 100644 --- a/ebpf/aya-ebpf/src/programs/mod.rs +++ b/ebpf/aya-ebpf/src/programs/mod.rs @@ -2,6 +2,7 @@ pub mod device; pub mod fentry; pub mod fexit; pub mod flow_dissector; +pub mod hid_bpf; pub mod lsm; pub mod perf_event; pub mod probe; @@ -14,6 +15,7 @@ pub mod sock; pub mod sock_addr; pub mod sock_ops; pub mod sockopt; +pub mod struct_ops; pub mod sysctl; pub mod tc; pub mod tp_btf; @@ -24,6 +26,12 @@ pub use device::DeviceContext; pub use fentry::FEntryContext; pub use fexit::FExitContext; pub use flow_dissector::FlowDissectorContext; +#[cfg(target_arch = "bpf")] +pub use hid_bpf::{AllocatedContext, kfunc}; +pub use hid_bpf::{ + BUS_BLUETOOTH, BUS_I2C, BUS_USB, HID_GROUP_ANY, HID_GROUP_GENERIC, HID_IGNORE_EVENT, + HidBpfContext, HidBpfData, HidBpfProbeArgs, HidClassRequest, HidReportType, hid_bpf_ctx, +}; pub use lsm::LsmContext; pub use perf_event::PerfEventContext; pub use probe::ProbeContext; @@ -36,6 +44,7 @@ pub use sock::SockContext; pub use sock_addr::SockAddrContext; pub use sock_ops::SockOpsContext; pub use sockopt::SockoptContext; +pub use struct_ops::StructOpsContext; pub use sysctl::SysctlContext; pub use tc::TcContext; pub use tp_btf::BtfTracePointContext; diff --git a/ebpf/aya-ebpf/src/programs/struct_ops.rs b/ebpf/aya-ebpf/src/programs/struct_ops.rs new file mode 100644 index 000000000..b0bb9c662 --- /dev/null +++ b/ebpf/aya-ebpf/src/programs/struct_ops.rs @@ -0,0 +1,40 @@ +use core::ffi::c_void; + +use crate::{Argument, EbpfContext, args::btf_arg}; + +/// Context provided to struct_ops programs. +/// +/// Struct ops programs implement kernel interfaces and receive context +/// appropriate to the specific callback being invoked. +pub struct StructOpsContext { + ctx: *mut c_void, +} + +impl StructOpsContext { + /// Creates a new context from a raw pointer. + pub fn new(ctx: *mut c_void) -> Self { + Self { ctx } + } + + /// Returns the `n`th argument passed to the struct_ops callback, starting from 0. + /// + /// # Examples + /// + /// ```no_run + /// # use aya_ebpf::programs::StructOpsContext; + /// unsafe fn my_struct_ops_callback(ctx: StructOpsContext) -> i32 { + /// let arg0: u64 = ctx.arg(0); + /// // Process the argument... + /// 0 + /// } + /// ``` + pub fn arg(&self, n: usize) -> T { + btf_arg(self, n) + } +} + +impl EbpfContext for StructOpsContext { + fn as_ptr(&self) -> *mut c_void { + self.ctx + } +} diff --git a/test/integration-ebpf/Cargo.toml b/test/integration-ebpf/Cargo.toml index 33715cfb8..4257a072d 100644 --- a/test/integration-ebpf/Cargo.toml +++ b/test/integration-ebpf/Cargo.toml @@ -107,3 +107,15 @@ path = "src/uprobe_cookie.rs" [[bin]] name = "perf_event_bp" path = "src/perf_event_bp.rs" + +[[bin]] +name = "printk_test" +path = "src/printk_test.rs" + +[[bin]] +name = "struct_ops_test" +path = "src/struct_ops_test.rs" + +[[bin]] +name = "hid_bpf_test" +path = "src/hid_bpf_test.rs" diff --git a/test/integration-ebpf/src/hid_bpf_test.rs b/test/integration-ebpf/src/hid_bpf_test.rs new file mode 100644 index 000000000..e79413943 --- /dev/null +++ b/test/integration-ebpf/src/hid_bpf_test.rs @@ -0,0 +1,44 @@ +//! HID-BPF test program demonstrating the HID-BPF macros. +//! +//! This program shows how to use the HID-BPF macros in Rust. +//! Note: This requires kernel 6.3+ with HID-BPF support. + +#![no_std] +#![no_main] +#![expect(unused_crate_dependencies, reason = "used in other bins")] + +use aya_ebpf::{ + helpers::bpf_printk, + macros::{hid_device_event, hid_hw_request, hid_rdesc_fixup}, + programs::HidBpfContext, +}; + +#[cfg(not(test))] +extern crate ebpf_panic; + +/// Device event handler - called for each HID input report. +#[hid_device_event] +pub(crate) fn hid_device_event_handler(_ctx: HidBpfContext) -> i32 { + unsafe { + bpf_printk!(b"hid_device_event called"); + } + 0 +} + +/// Report descriptor fixup - called at probe time. +#[hid_rdesc_fixup] +pub(crate) fn hid_rdesc_fixup_handler(_ctx: HidBpfContext) -> i32 { + unsafe { + bpf_printk!(b"hid_rdesc_fixup called"); + } + 0 +} + +/// Hardware request handler - called for feature reports etc. +#[hid_hw_request] +pub(crate) fn hid_hw_request_handler(_ctx: HidBpfContext) -> i32 { + unsafe { + bpf_printk!(b"hid_hw_request called"); + } + 0 +} diff --git a/test/integration-ebpf/src/printk_test.rs b/test/integration-ebpf/src/printk_test.rs new file mode 100644 index 000000000..1da9eb70f --- /dev/null +++ b/test/integration-ebpf/src/printk_test.rs @@ -0,0 +1,69 @@ +//! Integration test for bpf_printk variadic argument passing. +//! +//! This tests that PrintkArg correctly passes values to the bpf_trace_printk +//! kernel helper. The C ABI for variadic functions requires scalar values +//! (not arrays) to be passed by value in registers. + +#![no_std] +#![no_main] +#![expect(unused_crate_dependencies, reason = "used in other bins")] + +use aya_ebpf::{ + cty::c_long, + helpers::bpf_printk, + macros::uprobe, + programs::ProbeContext, +}; +#[cfg(not(test))] +extern crate ebpf_panic; + +/// Magic marker to identify our trace output. +const MARKER: u64 = 0xABCD_1234_5678_9ABC; + +/// Test values - chosen to be easily identifiable in trace output. +const TEST_U8: u8 = 42; +const TEST_U16: u16 = 0x1234; +const TEST_U32: u32 = 0xDEAD_BEEF; +const TEST_U64: u64 = 0x0123_4567_89AB_CDEF; +const TEST_I32: i32 = -12345; + +#[uprobe] +fn test_bpf_printk(_ctx: ProbeContext) -> Result<(), c_long> { + // Print marker so test can identify our output + // SAFETY: bpf_printk is safe with valid format string and matching args + unsafe { + bpf_printk!(b"PRINTK_TEST_MARKER:%lx", MARKER); + } + + // Test u8 (promoted to u64) + unsafe { + bpf_printk!(b"PRINTK_TEST_U8:%u", TEST_U8); + } + + // Test u16 (promoted to u64) + unsafe { + bpf_printk!(b"PRINTK_TEST_U16:%u", TEST_U16); + } + + // Test u32 + unsafe { + bpf_printk!(b"PRINTK_TEST_U32:%x", TEST_U32); + } + + // Test u64 + unsafe { + bpf_printk!(b"PRINTK_TEST_U64:%lx", TEST_U64); + } + + // Test i32 (negative value, sign-extended) + unsafe { + bpf_printk!(b"PRINTK_TEST_I32:%d", TEST_I32); + } + + // Test multiple arguments in one call + unsafe { + bpf_printk!(b"PRINTK_TEST_MULTI:%u,%x", TEST_U8, TEST_U32); + } + + Ok(()) +} diff --git a/test/integration-ebpf/src/struct_ops_test.rs b/test/integration-ebpf/src/struct_ops_test.rs new file mode 100644 index 000000000..b502838b3 --- /dev/null +++ b/test/integration-ebpf/src/struct_ops_test.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![expect(unused_crate_dependencies, reason = "used in other bins")] + +use aya_ebpf::{macros::struct_ops, programs::StructOpsContext}; +#[cfg(not(test))] +extern crate ebpf_panic; + +/// Test callback implementing a struct_ops member. +/// This tests basic struct_ops section parsing. +#[struct_ops] +pub(crate) fn struct_ops_test_callback(_ctx: StructOpsContext) -> i32 { + 0 +} + +/// A second callback to test multiple struct_ops programs in one object. +/// The name attribute allows the section name to differ from the function name. +#[struct_ops(name = "another_callback")] +pub(crate) fn struct_ops_second_callback(_ctx: StructOpsContext) -> i32 { + 1 +} + +/// A sleepable struct_ops callback for testing sleepable section parsing. +#[struct_ops(sleepable)] +pub(crate) fn struct_ops_sleepable_callback(_ctx: StructOpsContext) -> i32 { + 2 +} diff --git a/test/integration-test/src/lib.rs b/test/integration-test/src/lib.rs index 04d87bc6e..63b3dade2 100644 --- a/test/integration-test/src/lib.rs +++ b/test/integration-test/src/lib.rs @@ -60,6 +60,8 @@ bpf_file!( TWO_PROGS => "two_progs", XDP_SEC => "xdp_sec", UPROBE_COOKIE => "uprobe_cookie", + PRINTK_TEST => "printk_test", + STRUCT_OPS_TEST => "struct_ops_test", ); #[cfg(test)] diff --git a/test/integration-test/src/tests.rs b/test/integration-test/src/tests.rs index 4de831aad..7922cb765 100644 --- a/test/integration-test/src/tests.rs +++ b/test/integration-test/src/tests.rs @@ -12,6 +12,7 @@ mod lsm; mod map_pin; mod maps_disjoint; mod perf_event_bp; +mod printk; mod raw_tracepoint; mod rbpf; mod relocations; @@ -19,6 +20,7 @@ mod ring_buf; mod sk_storage; mod smoke; mod strncmp; +mod struct_ops; mod tcx; mod uprobe_cookie; mod xdp; diff --git a/test/integration-test/src/tests/printk.rs b/test/integration-test/src/tests/printk.rs new file mode 100644 index 000000000..9f3f9a68d --- /dev/null +++ b/test/integration-test/src/tests/printk.rs @@ -0,0 +1,122 @@ +//! Integration test for bpf_printk variadic argument passing. +//! +//! This test verifies that PrintkArg correctly passes values to bpf_trace_printk. +//! It reads from the kernel trace buffer to verify the printed values match. +//! +//! NOTE: This test requires that no other process is reading trace_pipe, as that +//! would consume the trace entries before this test can verify them. + +use std::{fs, thread, time::Duration}; + +use aya::{Ebpf, programs::UProbe}; + +/// Expected values - must match the eBPF program constants. +const MARKER: u64 = 0xABCD_1234_5678_9ABC; +const TEST_U8: u8 = 42; +const TEST_U16: u16 = 0x1234; +const TEST_U32: u32 = 0xDEAD_BEEF; +const TEST_U64: u64 = 0x0123_4567_89AB_CDEF; +const TEST_I32: i32 = -12345; + +#[test_log::test] +fn bpf_printk_variadic_args() { + // Check if trace is accessible (requires root or tracing group) + let trace_path = "/sys/kernel/debug/tracing/trace"; + if !std::path::Path::new(trace_path).exists() { + eprintln!("skipping test: {trace_path} not accessible (need root or tracing group)"); + return; + } + + // Clear the trace buffer first + if fs::write(trace_path, "").is_err() { + eprintln!("skipping test: cannot write to trace (need root)"); + return; + } + + let mut bpf = Ebpf::load(crate::PRINTK_TEST).unwrap(); + + let prog: &mut UProbe = bpf + .program_mut("test_bpf_printk") + .unwrap() + .try_into() + .unwrap(); + prog.load().unwrap(); + + let _link = prog + .attach("trigger_bpf_printk", "/proc/self/exe", None) + .unwrap(); + + // Trigger the BPF program + trigger_bpf_printk(); + + // Give the kernel a moment to write trace output + thread::sleep(Duration::from_millis(100)); + + // Read trace output + let trace_content = fs::read_to_string(trace_path).unwrap(); + + // Check if trace is empty (likely consumed by trace_pipe reader) + if trace_content.contains("entries-in-buffer/entries-written: 0/0") + || !trace_content.contains("bpf_trace_printk") + { + eprintln!( + "skipping test: no printk output in trace buffer\n\ + This usually means another process is reading trace_pipe.\n\ + Close any 'cat trace_pipe' commands to run this test." + ); + return; + } + + // Find our marker first to ensure we're reading our output + let marker_expected = format!("PRINTK_TEST_MARKER:{:x}", MARKER); + assert!( + trace_content.contains(&marker_expected), + "marker not found in trace output - expected '{marker_expected}'\ntrace:\n{trace_content}" + ); + + // Verify each test value + let u8_expected = format!("PRINTK_TEST_U8:{}", TEST_U8); + assert!( + trace_content.contains(&u8_expected), + "u8 test failed - expected '{u8_expected}'\ntrace:\n{trace_content}" + ); + + let u16_expected = format!("PRINTK_TEST_U16:{}", TEST_U16); + assert!( + trace_content.contains(&u16_expected), + "u16 test failed - expected '{u16_expected}'\ntrace:\n{trace_content}" + ); + + let u32_expected = format!("PRINTK_TEST_U32:{:x}", TEST_U32); + assert!( + trace_content.contains(&u32_expected), + "u32 test failed - expected '{u32_expected}'\ntrace:\n{trace_content}" + ); + + let u64_expected = format!("PRINTK_TEST_U64:{:x}", TEST_U64); + assert!( + trace_content.contains(&u64_expected), + "u64 test failed - expected '{u64_expected}'\ntrace:\n{trace_content}" + ); + + let i32_expected = format!("PRINTK_TEST_I32:{}", TEST_I32); + assert!( + trace_content.contains(&i32_expected), + "i32 test failed - expected '{i32_expected}'\ntrace:\n{trace_content}" + ); + + // Verify multi-arg call + let multi_expected = format!("PRINTK_TEST_MULTI:{},{:x}", TEST_U8, TEST_U32); + assert!( + trace_content.contains(&multi_expected), + "multi-arg test failed - expected '{multi_expected}'\ntrace:\n{trace_content}" + ); + + eprintln!("bpf_printk variadic argument test passed!"); +} + +#[unsafe(no_mangle)] +#[inline(never)] +extern "C" fn trigger_bpf_printk() { + core::hint::black_box(()); +} diff --git a/test/integration-test/src/tests/rbpf.rs b/test/integration-test/src/tests/rbpf.rs index faa7a0349..93cfe2b8f 100644 --- a/test/integration-test/src/tests/rbpf.rs +++ b/test/integration-test/src/tests/rbpf.rs @@ -91,7 +91,7 @@ fn use_map_with_rbpf() { ) .expect("Relocation failed"); // Actually there is no local function call involved. - object.relocate_calls(&text_sections).unwrap(); + object.relocate_calls(&text_sections, None).unwrap(); // Executes the program assert_eq!(object.programs.len(), 1); diff --git a/test/integration-test/src/tests/struct_ops.rs b/test/integration-test/src/tests/struct_ops.rs new file mode 100644 index 000000000..74d405f74 --- /dev/null +++ b/test/integration-test/src/tests/struct_ops.rs @@ -0,0 +1,195 @@ +use aya::{Btf, Ebpf, programs::StructOps}; + +/// Test that struct_ops programs are parsed correctly from the ELF. +/// +/// Verifies: +/// - Multiple struct_ops programs can exist in one object +/// - Programs are accessed by their Rust function name (not the section suffix) +/// - Programs can be retrieved by name +#[test_log::test] +fn struct_ops_parse_multiple_programs() { + let ebpf = Ebpf::load(crate::STRUCT_OPS_TEST).unwrap(); + + // Test that all three struct_ops programs are found + // Note: Programs are accessed by their Rust function names + let programs: Vec<_> = ebpf.programs().map(|(name, _)| name).collect(); + + assert!( + programs.contains(&"struct_ops_test_callback"), + "missing struct_ops_test_callback, found: {programs:?}" + ); + // The renamed callback uses the function name, not the section suffix + assert!( + programs.contains(&"struct_ops_second_callback"), + "missing struct_ops_second_callback, found: {programs:?}" + ); + assert!( + programs.contains(&"struct_ops_sleepable_callback"), + "missing struct_ops_sleepable_callback, found: {programs:?}" + ); +} + +/// Test that struct_ops programs have the correct program type. +#[test_log::test] +fn struct_ops_program_type() { + let ebpf = Ebpf::load(crate::STRUCT_OPS_TEST).unwrap(); + + // Programs are accessed by their Rust function names + for name in [ + "struct_ops_test_callback", + "struct_ops_second_callback", + "struct_ops_sleepable_callback", + ] { + let prog = ebpf.program(name).expect(&format!("program {name} not found")); + + assert!( + matches!(prog.prog_type(), aya::programs::ProgramType::StructOps), + "expected StructOps program type for {name}" + ); + } +} + +/// Test that struct_ops programs can be converted to the StructOps type. +#[test_log::test] +fn struct_ops_type_conversion() { + let mut ebpf = Ebpf::load(crate::STRUCT_OPS_TEST).unwrap(); + + // Each program should be convertible to StructOps + let prog: &mut StructOps = ebpf + .program_mut("struct_ops_test_callback") + .expect("program not found") + .try_into() + .expect("failed to convert to StructOps"); + + // Verify the member name matches the section name + assert_eq!( + prog.member_name(), + "struct_ops_test_callback", + "member_name should match the program's section suffix" + ); +} + +/// Test that the member_name reflects the struct_ops section suffix. +/// +/// When using `#[struct_ops(name = "another_callback")]`, the program is +/// accessed by its Rust function name, but member_name() returns the +/// section suffix (the name attribute value). +#[test_log::test] +fn struct_ops_renamed_member_name() { + let mut ebpf = Ebpf::load(crate::STRUCT_OPS_TEST).unwrap(); + + // Access the program by its Rust function name + let prog: &mut StructOps = ebpf + .program_mut("struct_ops_second_callback") + .expect("program not found") + .try_into() + .expect("failed to convert to StructOps"); + + // The member_name should be "another_callback" (from the name attribute), + // which is the struct_ops callback name used for BTF lookups + assert_eq!( + prog.member_name(), + "another_callback", + "member_name should match the section suffix (name attribute), not the function name" + ); +} + +/// Test that kernel BTF is available and contains common struct_ops types. +/// +/// This test verifies that the BTF infrastructure works, which is required +/// for struct_ops loading. It checks for well-known struct_ops types that +/// should be present in modern kernels. +#[test_log::test] +fn struct_ops_btf_availability() { + let btf = match Btf::from_sys_fs() { + Ok(btf) => btf, + Err(e) => { + eprintln!("skipping test - kernel BTF not available: {e}"); + return; + } + }; + + // tcp_congestion_ops has been available since kernel 5.6 + // This is a basic sanity check that BTF type lookup works + let result = btf.id_by_type_name_kind("tcp_congestion_ops", aya_obj::btf::BtfKind::Struct); + + match result { + Ok(type_id) => { + assert!(type_id > 0, "tcp_congestion_ops should have a valid type ID"); + } + Err(e) => { + // This is not necessarily an error - older kernels may not have it + eprintln!("tcp_congestion_ops not found in BTF (may be expected on older kernels): {e}"); + } + } +} + +/// Test that struct member lookup works in BTF. +/// +/// This is important for struct_ops because we need to resolve member +/// names to indices when loading programs. +#[test_log::test] +fn struct_ops_btf_member_lookup() { + let btf = match Btf::from_sys_fs() { + Ok(btf) => btf, + Err(e) => { + eprintln!("skipping test - kernel BTF not available: {e}"); + return; + } + }; + + // Look up tcp_congestion_ops struct + let struct_id = match btf.id_by_type_name_kind("tcp_congestion_ops", aya_obj::btf::BtfKind::Struct) { + Ok(id) => id, + Err(e) => { + eprintln!("skipping test - tcp_congestion_ops not in BTF: {e}"); + return; + } + }; + + // tcp_congestion_ops should have a "name" member (char[16] for the algorithm name) + let name_idx = btf.struct_member_index(struct_id, "name"); + assert!( + name_idx.is_ok(), + "tcp_congestion_ops should have a 'name' member: {:?}", + name_idx.err() + ); + + // It should also have "ssthresh" which is a required callback + let ssthresh_idx = btf.struct_member_index(struct_id, "ssthresh"); + assert!( + ssthresh_idx.is_ok(), + "tcp_congestion_ops should have a 'ssthresh' member: {:?}", + ssthresh_idx.err() + ); + + // Member indices should be different + assert_ne!( + name_idx.unwrap(), + ssthresh_idx.unwrap(), + "different members should have different indices" + ); +} + +/// Test error handling for non-existent struct_ops type. +#[test_log::test] +fn struct_ops_btf_unknown_type() { + let btf = match Btf::from_sys_fs() { + Ok(btf) => btf, + Err(e) => { + eprintln!("skipping test - kernel BTF not available: {e}"); + return; + } + }; + + // Looking up a non-existent type should fail + let result = btf.id_by_type_name_kind( + "definitely_not_a_real_struct_ops_type_12345", + aya_obj::btf::BtfKind::Struct, + ); + + assert!( + result.is_err(), + "looking up non-existent type should fail" + ); +} diff --git a/xtask/public-api/aya-ebpf-macros.txt b/xtask/public-api/aya-ebpf-macros.txt index c0e002411..dc4f16af8 100644 --- a/xtask/public-api/aya-ebpf-macros.txt +++ b/xtask/public-api/aya-ebpf-macros.txt @@ -11,6 +11,10 @@ pub proc macro aya_ebpf_macros::#[classifier] pub proc macro aya_ebpf_macros::#[fentry] pub proc macro aya_ebpf_macros::#[fexit] pub proc macro aya_ebpf_macros::#[flow_dissector] +pub proc macro aya_ebpf_macros::#[hid_device_event] +pub proc macro aya_ebpf_macros::#[hid_hw_output_report] +pub proc macro aya_ebpf_macros::#[hid_hw_request] +pub proc macro aya_ebpf_macros::#[hid_rdesc_fixup] pub proc macro aya_ebpf_macros::#[kprobe] pub proc macro aya_ebpf_macros::#[kretprobe] pub proc macro aya_ebpf_macros::#[lsm] @@ -24,6 +28,7 @@ pub proc macro aya_ebpf_macros::#[sock_ops] pub proc macro aya_ebpf_macros::#[socket_filter] pub proc macro aya_ebpf_macros::#[stream_parser] pub proc macro aya_ebpf_macros::#[stream_verdict] +pub proc macro aya_ebpf_macros::#[struct_ops] pub proc macro aya_ebpf_macros::#[tracepoint] pub proc macro aya_ebpf_macros::#[uprobe] pub proc macro aya_ebpf_macros::#[uretprobe] diff --git a/xtask/public-api/aya-ebpf.txt b/xtask/public-api/aya-ebpf.txt index e14196ca5..67c66dc60 100644 --- a/xtask/public-api/aya-ebpf.txt +++ b/xtask/public-api/aya-ebpf.txt @@ -1778,6 +1778,204 @@ impl core::borrow::BorrowMut for aya_ebpf::programs::flow_dissector::FlowD pub fn aya_ebpf::programs::flow_dissector::FlowDissectorContext::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_ebpf::programs::flow_dissector::FlowDissectorContext pub fn aya_ebpf::programs::flow_dissector::FlowDissectorContext::from(t: T) -> T +pub mod aya_ebpf::programs::hid_bpf +#[repr(u32)] pub enum aya_ebpf::programs::hid_bpf::HidClassRequest +pub aya_ebpf::programs::hid_bpf::HidClassRequest::GetIdle = 2 +pub aya_ebpf::programs::hid_bpf::HidClassRequest::GetProtocol = 3 +pub aya_ebpf::programs::hid_bpf::HidClassRequest::GetReport = 1 +pub aya_ebpf::programs::hid_bpf::HidClassRequest::SetIdle = 10 +pub aya_ebpf::programs::hid_bpf::HidClassRequest::SetProtocol = 11 +pub aya_ebpf::programs::hid_bpf::HidClassRequest::SetReport = 9 +impl core::clone::Clone for aya_ebpf::programs::hid_bpf::HidClassRequest +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::clone(&self) -> aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::cmp::Eq for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::cmp::PartialEq for aya_ebpf::programs::hid_bpf::HidClassRequest +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::eq(&self, other: &aya_ebpf::programs::hid_bpf::HidClassRequest) -> bool +impl core::fmt::Debug for aya_ebpf::programs::hid_bpf::HidClassRequest +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::StructuralPartialEq for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::Send for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::Sync for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidClassRequest where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidClassRequest where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidClassRequest::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidClassRequest where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidClassRequest::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidClassRequest where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidClassRequest where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidClassRequest where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_ebpf::programs::hid_bpf::HidClassRequest where T: core::clone::Clone +pub unsafe fn aya_ebpf::programs::hid_bpf::HidClassRequest::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidClassRequest +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::from(t: T) -> T +#[repr(u32)] pub enum aya_ebpf::programs::hid_bpf::HidReportType +pub aya_ebpf::programs::hid_bpf::HidReportType::Feature = 2 +pub aya_ebpf::programs::hid_bpf::HidReportType::Input = 0 +pub aya_ebpf::programs::hid_bpf::HidReportType::Output = 1 +impl core::clone::Clone for aya_ebpf::programs::hid_bpf::HidReportType +pub fn aya_ebpf::programs::hid_bpf::HidReportType::clone(&self) -> aya_ebpf::programs::hid_bpf::HidReportType +impl core::cmp::Eq for aya_ebpf::programs::hid_bpf::HidReportType +impl core::cmp::PartialEq for aya_ebpf::programs::hid_bpf::HidReportType +pub fn aya_ebpf::programs::hid_bpf::HidReportType::eq(&self, other: &aya_ebpf::programs::hid_bpf::HidReportType) -> bool +impl core::fmt::Debug for aya_ebpf::programs::hid_bpf::HidReportType +pub fn aya_ebpf::programs::hid_bpf::HidReportType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::StructuralPartialEq for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::Send for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::Sync for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidReportType +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidReportType +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidReportType +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidReportType where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidReportType::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidReportType where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidReportType::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidReportType::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidReportType where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidReportType::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidReportType::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidReportType where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidReportType::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidReportType where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidReportType::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidReportType where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidReportType::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_ebpf::programs::hid_bpf::HidReportType where T: core::clone::Clone +pub unsafe fn aya_ebpf::programs::hid_bpf::HidReportType::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidReportType +pub fn aya_ebpf::programs::hid_bpf::HidReportType::from(t: T) -> T +#[repr(transparent)] pub struct aya_ebpf::programs::hid_bpf::HidBpfContext +impl aya_ebpf::programs::hid_bpf::HidBpfContext +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::allocated_size(&self) -> u32 +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::as_ptr(&self) -> *mut aya_ebpf::programs::hid_bpf::hid_bpf_ctx +pub unsafe fn aya_ebpf::programs::hid_bpf::HidBpfContext::new(ctx: *mut aya_ebpf::programs::hid_bpf::hid_bpf_ctx) -> Self +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::retval(&self) -> i32 +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::set_retval(&mut self, val: i32) +impl aya_ebpf::EbpfContext for aya_ebpf::programs::hid_bpf::HidBpfContext +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::as_ptr(&self) -> *mut core::ffi::c_void +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidBpfContext +impl !core::marker::Send for aya_ebpf::programs::hid_bpf::HidBpfContext +impl !core::marker::Sync for aya_ebpf::programs::hid_bpf::HidBpfContext +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidBpfContext +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfContext +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfContext +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidBpfContext where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidBpfContext where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidBpfContext::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidBpfContext where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidBpfContext::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidBpfContext where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidBpfContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidBpfContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidBpfContext +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::from(t: T) -> T +pub struct aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl aya_ebpf::programs::hid_bpf::HidBpfData<'_> +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::copy_from_slice(&mut self, offset: usize, src: &[u8]) -> bool +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::get(&self, idx: usize) -> core::option::Option +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::is_empty(&self) -> bool +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::len(&self) -> usize +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::set(&mut self, idx: usize, val: u8) -> bool +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::starts_with(&self, pattern: &[u8]) -> bool +impl<'a> core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> !core::marker::Send for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> !core::marker::Sync for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> !core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidBpfData<'a>::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidBpfData<'a>::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::from(t: T) -> T +#[repr(C)] pub struct aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +pub aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::hid: u32 +pub aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::rdesc: [u8; 4096] +pub aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::rdesc_size: u32 +pub aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::retval: i32 +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::marker::Send for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::marker::Sync for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::from(t: T) -> T +#[repr(C)] pub struct aya_ebpf::programs::hid_bpf::hid_bpf_ctx +pub aya_ebpf::programs::hid_bpf::hid_bpf_ctx::allocated_size: u32 +pub aya_ebpf::programs::hid_bpf::hid_bpf_ctx::hid: u64 +pub aya_ebpf::programs::hid_bpf::hid_bpf_ctx::retval: i32 +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::marker::Send for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::marker::Sync for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::convert::Into for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::hid_bpf_ctx::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::hid_bpf_ctx::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::from(t: T) -> T +pub const aya_ebpf::programs::hid_bpf::BUS_BLUETOOTH: u16 +pub const aya_ebpf::programs::hid_bpf::BUS_I2C: u16 +pub const aya_ebpf::programs::hid_bpf::BUS_USB: u16 +pub const aya_ebpf::programs::hid_bpf::HID_GROUP_ANY: u16 +pub const aya_ebpf::programs::hid_bpf::HID_GROUP_GENERIC: u16 +pub const aya_ebpf::programs::hid_bpf::HID_IGNORE_EVENT: i32 pub mod aya_ebpf::programs::lsm pub struct aya_ebpf::programs::lsm::LsmContext impl aya_ebpf::programs::lsm::LsmContext @@ -2208,6 +2406,35 @@ impl core::borrow::BorrowMut for aya_ebpf::programs::sockopt::SockoptConte pub fn aya_ebpf::programs::sockopt::SockoptContext::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_ebpf::programs::sockopt::SockoptContext pub fn aya_ebpf::programs::sockopt::SockoptContext::from(t: T) -> T +pub mod aya_ebpf::programs::struct_ops +pub struct aya_ebpf::programs::struct_ops::StructOpsContext +impl aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::arg(&self, n: usize) -> T +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::new(ctx: *mut core::ffi::c_void) -> Self +impl aya_ebpf::EbpfContext for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::as_ptr(&self) -> *mut core::ffi::c_void +impl core::marker::Freeze for aya_ebpf::programs::struct_ops::StructOpsContext +impl !core::marker::Send for aya_ebpf::programs::struct_ops::StructOpsContext +impl !core::marker::Sync for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::marker::Unpin for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::convert::Into for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::From +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::Into +pub type aya_ebpf::programs::struct_ops::StructOpsContext::Error = core::convert::Infallible +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::TryFrom +pub type aya_ebpf::programs::struct_ops::StructOpsContext::Error = >::Error +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::struct_ops::StructOpsContext where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::struct_ops::StructOpsContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::struct_ops::StructOpsContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::from(t: T) -> T pub mod aya_ebpf::programs::sysctl pub struct aya_ebpf::programs::sysctl::SysctlContext pub aya_ebpf::programs::sysctl::SysctlContext::sysctl: *mut aya_ebpf_bindings::x86_64::bindings::bpf_sysctl @@ -2376,6 +2603,83 @@ impl core::borrow::BorrowMut for aya_ebpf::programs::xdp::XdpContext where pub fn aya_ebpf::programs::xdp::XdpContext::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_ebpf::programs::xdp::XdpContext pub fn aya_ebpf::programs::xdp::XdpContext::from(t: T) -> T +#[repr(u32)] pub enum aya_ebpf::programs::HidClassRequest +pub aya_ebpf::programs::HidClassRequest::GetIdle = 2 +pub aya_ebpf::programs::HidClassRequest::GetProtocol = 3 +pub aya_ebpf::programs::HidClassRequest::GetReport = 1 +pub aya_ebpf::programs::HidClassRequest::SetIdle = 10 +pub aya_ebpf::programs::HidClassRequest::SetProtocol = 11 +pub aya_ebpf::programs::HidClassRequest::SetReport = 9 +impl core::clone::Clone for aya_ebpf::programs::hid_bpf::HidClassRequest +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::clone(&self) -> aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::cmp::Eq for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::cmp::PartialEq for aya_ebpf::programs::hid_bpf::HidClassRequest +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::eq(&self, other: &aya_ebpf::programs::hid_bpf::HidClassRequest) -> bool +impl core::fmt::Debug for aya_ebpf::programs::hid_bpf::HidClassRequest +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::StructuralPartialEq for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::Send for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::Sync for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidClassRequest +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidClassRequest where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidClassRequest where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidClassRequest::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidClassRequest where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidClassRequest::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidClassRequest where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidClassRequest where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidClassRequest where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_ebpf::programs::hid_bpf::HidClassRequest where T: core::clone::Clone +pub unsafe fn aya_ebpf::programs::hid_bpf::HidClassRequest::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidClassRequest +pub fn aya_ebpf::programs::hid_bpf::HidClassRequest::from(t: T) -> T +#[repr(u32)] pub enum aya_ebpf::programs::HidReportType +pub aya_ebpf::programs::HidReportType::Feature = 2 +pub aya_ebpf::programs::HidReportType::Input = 0 +pub aya_ebpf::programs::HidReportType::Output = 1 +impl core::clone::Clone for aya_ebpf::programs::hid_bpf::HidReportType +pub fn aya_ebpf::programs::hid_bpf::HidReportType::clone(&self) -> aya_ebpf::programs::hid_bpf::HidReportType +impl core::cmp::Eq for aya_ebpf::programs::hid_bpf::HidReportType +impl core::cmp::PartialEq for aya_ebpf::programs::hid_bpf::HidReportType +pub fn aya_ebpf::programs::hid_bpf::HidReportType::eq(&self, other: &aya_ebpf::programs::hid_bpf::HidReportType) -> bool +impl core::fmt::Debug for aya_ebpf::programs::hid_bpf::HidReportType +pub fn aya_ebpf::programs::hid_bpf::HidReportType::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Copy for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::StructuralPartialEq for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::Send for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::Sync for aya_ebpf::programs::hid_bpf::HidReportType +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidReportType +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidReportType +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidReportType +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidReportType where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidReportType::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidReportType where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidReportType::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidReportType::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidReportType where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidReportType::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidReportType::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidReportType where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidReportType::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidReportType where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidReportType::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidReportType where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidReportType::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_ebpf::programs::hid_bpf::HidReportType where T: core::clone::Clone +pub unsafe fn aya_ebpf::programs::hid_bpf::HidReportType::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidReportType +pub fn aya_ebpf::programs::hid_bpf::HidReportType::from(t: T) -> T pub struct aya_ebpf::programs::BtfTracePointContext impl aya_ebpf::programs::tp_btf::BtfTracePointContext pub fn aya_ebpf::programs::tp_btf::BtfTracePointContext::arg(&self, n: usize) -> T @@ -2519,6 +2823,94 @@ impl core::borrow::BorrowMut for aya_ebpf::programs::flow_dissector::FlowD pub fn aya_ebpf::programs::flow_dissector::FlowDissectorContext::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_ebpf::programs::flow_dissector::FlowDissectorContext pub fn aya_ebpf::programs::flow_dissector::FlowDissectorContext::from(t: T) -> T +#[repr(transparent)] pub struct aya_ebpf::programs::HidBpfContext +impl aya_ebpf::programs::hid_bpf::HidBpfContext +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::allocated_size(&self) -> u32 +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::as_ptr(&self) -> *mut aya_ebpf::programs::hid_bpf::hid_bpf_ctx +pub unsafe fn aya_ebpf::programs::hid_bpf::HidBpfContext::new(ctx: *mut aya_ebpf::programs::hid_bpf::hid_bpf_ctx) -> Self +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::retval(&self) -> i32 +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::set_retval(&mut self, val: i32) +impl aya_ebpf::EbpfContext for aya_ebpf::programs::hid_bpf::HidBpfContext +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::as_ptr(&self) -> *mut core::ffi::c_void +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidBpfContext +impl !core::marker::Send for aya_ebpf::programs::hid_bpf::HidBpfContext +impl !core::marker::Sync for aya_ebpf::programs::hid_bpf::HidBpfContext +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidBpfContext +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfContext +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfContext +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidBpfContext where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidBpfContext where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidBpfContext::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidBpfContext where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidBpfContext::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidBpfContext where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidBpfContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidBpfContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidBpfContext +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::from(t: T) -> T +pub struct aya_ebpf::programs::HidBpfData<'a> +impl aya_ebpf::programs::hid_bpf::HidBpfData<'_> +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::copy_from_slice(&mut self, offset: usize, src: &[u8]) -> bool +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::get(&self, idx: usize) -> core::option::Option +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::is_empty(&self) -> bool +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::len(&self) -> usize +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::set(&mut self, idx: usize, val: u8) -> bool +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'_>::starts_with(&self, pattern: &[u8]) -> bool +impl<'a> core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> !core::marker::Send for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> !core::marker::Sync for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl<'a> !core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidBpfData<'a>::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidBpfData<'a>::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidBpfData<'a> where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidBpfData<'a> +pub fn aya_ebpf::programs::hid_bpf::HidBpfData<'a>::from(t: T) -> T +#[repr(C)] pub struct aya_ebpf::programs::HidBpfProbeArgs +pub aya_ebpf::programs::HidBpfProbeArgs::hid: u32 +pub aya_ebpf::programs::HidBpfProbeArgs::rdesc: [u8; 4096] +pub aya_ebpf::programs::HidBpfProbeArgs::rdesc_size: u32 +pub aya_ebpf::programs::HidBpfProbeArgs::retval: i32 +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::marker::Send for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::marker::Sync for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +impl core::convert::Into for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::hid_bpf::HidBpfProbeArgs +pub fn aya_ebpf::programs::hid_bpf::HidBpfProbeArgs::from(t: T) -> T pub struct aya_ebpf::programs::LsmContext impl aya_ebpf::programs::lsm::LsmContext pub fn aya_ebpf::programs::lsm::LsmContext::arg(&self, n: usize) -> T @@ -2888,6 +3280,34 @@ impl core::borrow::BorrowMut for aya_ebpf::programs::sockopt::SockoptConte pub fn aya_ebpf::programs::sockopt::SockoptContext::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_ebpf::programs::sockopt::SockoptContext pub fn aya_ebpf::programs::sockopt::SockoptContext::from(t: T) -> T +pub struct aya_ebpf::programs::StructOpsContext +impl aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::arg(&self, n: usize) -> T +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::new(ctx: *mut core::ffi::c_void) -> Self +impl aya_ebpf::EbpfContext for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::as_ptr(&self) -> *mut core::ffi::c_void +impl core::marker::Freeze for aya_ebpf::programs::struct_ops::StructOpsContext +impl !core::marker::Send for aya_ebpf::programs::struct_ops::StructOpsContext +impl !core::marker::Sync for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::marker::Unpin for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::struct_ops::StructOpsContext +impl core::convert::Into for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::From +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::Into +pub type aya_ebpf::programs::struct_ops::StructOpsContext::Error = core::convert::Infallible +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::struct_ops::StructOpsContext where U: core::convert::TryFrom +pub type aya_ebpf::programs::struct_ops::StructOpsContext::Error = >::Error +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::struct_ops::StructOpsContext where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::struct_ops::StructOpsContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::struct_ops::StructOpsContext where T: ?core::marker::Sized +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::from(t: T) -> T pub struct aya_ebpf::programs::SysctlContext pub aya_ebpf::programs::SysctlContext::sysctl: *mut aya_ebpf_bindings::x86_64::bindings::bpf_sysctl impl aya_ebpf::programs::sysctl::SysctlContext @@ -3023,6 +3443,38 @@ impl core::borrow::BorrowMut for aya_ebpf::programs::xdp::XdpContext where pub fn aya_ebpf::programs::xdp::XdpContext::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya_ebpf::programs::xdp::XdpContext pub fn aya_ebpf::programs::xdp::XdpContext::from(t: T) -> T +#[repr(C)] pub struct aya_ebpf::programs::hid_bpf_ctx +pub aya_ebpf::programs::hid_bpf_ctx::allocated_size: u32 +pub aya_ebpf::programs::hid_bpf_ctx::hid: u64 +pub aya_ebpf::programs::hid_bpf_ctx::retval: i32 +impl core::marker::Freeze for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::marker::Send for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::marker::Sync for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::marker::Unpin for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::panic::unwind_safe::RefUnwindSafe for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::panic::unwind_safe::UnwindSafe for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +impl core::convert::Into for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where U: core::convert::From +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::into(self) -> U +impl core::convert::TryFrom for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where U: core::convert::Into +pub type aya_ebpf::programs::hid_bpf::hid_bpf_ctx::Error = core::convert::Infallible +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where U: core::convert::TryFrom +pub type aya_ebpf::programs::hid_bpf::hid_bpf_ctx::Error = >::Error +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where T: 'static + ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_ebpf::programs::hid_bpf::hid_bpf_ctx where T: ?core::marker::Sized +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya_ebpf::programs::hid_bpf::hid_bpf_ctx +pub fn aya_ebpf::programs::hid_bpf::hid_bpf_ctx::from(t: T) -> T +pub const aya_ebpf::programs::BUS_BLUETOOTH: u16 +pub const aya_ebpf::programs::BUS_I2C: u16 +pub const aya_ebpf::programs::BUS_USB: u16 +pub const aya_ebpf::programs::HID_GROUP_ANY: u16 +pub const aya_ebpf::programs::HID_GROUP_GENERIC: u16 +pub const aya_ebpf::programs::HID_IGNORE_EVENT: i32 pub macro aya_ebpf::bpf_printk! pub macro aya_ebpf::btf_map_def! pub const aya_ebpf::TASK_COMM_LEN: usize @@ -3043,6 +3495,8 @@ impl aya_ebpf::EbpfContext for aya_ebpf::programs::fexit::FExitContext pub fn aya_ebpf::programs::fexit::FExitContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::flow_dissector::FlowDissectorContext pub fn aya_ebpf::programs::flow_dissector::FlowDissectorContext::as_ptr(&self) -> *mut aya_ebpf_cty::c_void +impl aya_ebpf::EbpfContext for aya_ebpf::programs::hid_bpf::HidBpfContext +pub fn aya_ebpf::programs::hid_bpf::HidBpfContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::lsm::LsmContext pub fn aya_ebpf::programs::lsm::LsmContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::perf_event::PerfEventContext @@ -3067,6 +3521,8 @@ impl aya_ebpf::EbpfContext for aya_ebpf::programs::sock_ops::SockOpsContext pub fn aya_ebpf::programs::sock_ops::SockOpsContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::sockopt::SockoptContext pub fn aya_ebpf::programs::sockopt::SockoptContext::as_ptr(&self) -> *mut core::ffi::c_void +impl aya_ebpf::EbpfContext for aya_ebpf::programs::struct_ops::StructOpsContext +pub fn aya_ebpf::programs::struct_ops::StructOpsContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::sysctl::SysctlContext pub fn aya_ebpf::programs::sysctl::SysctlContext::as_ptr(&self) -> *mut core::ffi::c_void impl aya_ebpf::EbpfContext for aya_ebpf::programs::tc::TcContext diff --git a/xtask/public-api/aya-obj.txt b/xtask/public-api/aya-obj.txt index 5e59c5ea2..bb98d50da 100644 --- a/xtask/public-api/aya-obj.txt +++ b/xtask/public-api/aya-obj.txt @@ -363,7 +363,10 @@ pub fn aya_obj::btf::Btf::id_by_type_name_kind(&self, name: &str, kind: aya_obj: pub fn aya_obj::btf::Btf::new() -> Self pub fn aya_obj::btf::Btf::parse(data: &[u8], endianness: object::endian::Endianness) -> core::result::Result pub fn aya_obj::btf::Btf::parse_file>(path: P, endianness: object::endian::Endianness) -> core::result::Result +pub fn aya_obj::btf::Btf::struct_member_byte_offset(&self, struct_type_id: u32, member_name: &str) -> core::result::Result +pub fn aya_obj::btf::Btf::struct_member_index(&self, struct_type_id: u32, member_name: &str) -> core::result::Result pub fn aya_obj::btf::Btf::to_bytes(&self) -> alloc::vec::Vec +pub fn aya_obj::btf::Btf::type_size(&self, root_type_id: u32) -> core::result::Result impl core::clone::Clone for aya_obj::btf::Btf pub fn aya_obj::btf::Btf::clone(&self) -> aya_obj::btf::Btf impl core::default::Default for aya_obj::btf::Btf @@ -8202,7 +8205,9 @@ pub mod aya_obj::maps pub enum aya_obj::maps::Map pub aya_obj::maps::Map::Btf(aya_obj::maps::BtfMap) pub aya_obj::maps::Map::Legacy(aya_obj::maps::LegacyMap) +pub aya_obj::maps::Map::StructOps(aya_obj::maps::StructOpsMap) impl aya_obj::maps::Map +pub fn aya_obj::maps::Map::btf_value_type_id(&self) -> core::option::Option pub fn aya_obj::maps::Map::data(&self) -> &[u8] pub fn aya_obj::maps::Map::data_mut(&mut self) -> &mut alloc::vec::Vec pub fn aya_obj::maps::Map::key_size(&self) -> u32 @@ -8439,6 +8444,83 @@ impl core::clone::CloneToUninit for aya_obj::maps::LegacyMap where T: core::c pub unsafe fn aya_obj::maps::LegacyMap::clone_to_uninit(&self, dest: *mut u8) impl core::convert::From for aya_obj::maps::LegacyMap pub fn aya_obj::maps::LegacyMap::from(t: T) -> T +pub struct aya_obj::maps::StructOpsFuncInfo +pub aya_obj::maps::StructOpsFuncInfo::member_name: alloc::string::String +pub aya_obj::maps::StructOpsFuncInfo::member_offset: u32 +pub aya_obj::maps::StructOpsFuncInfo::prog_name: alloc::string::String +impl core::clone::Clone for aya_obj::maps::StructOpsFuncInfo +pub fn aya_obj::maps::StructOpsFuncInfo::clone(&self) -> aya_obj::maps::StructOpsFuncInfo +impl core::fmt::Debug for aya_obj::maps::StructOpsFuncInfo +pub fn aya_obj::maps::StructOpsFuncInfo::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya_obj::maps::StructOpsFuncInfo +impl core::marker::Send for aya_obj::maps::StructOpsFuncInfo +impl core::marker::Sync for aya_obj::maps::StructOpsFuncInfo +impl core::marker::Unpin for aya_obj::maps::StructOpsFuncInfo +impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::maps::StructOpsFuncInfo +impl core::panic::unwind_safe::UnwindSafe for aya_obj::maps::StructOpsFuncInfo +impl core::convert::Into for aya_obj::maps::StructOpsFuncInfo where U: core::convert::From +pub fn aya_obj::maps::StructOpsFuncInfo::into(self) -> U +impl core::convert::TryFrom for aya_obj::maps::StructOpsFuncInfo where U: core::convert::Into +pub type aya_obj::maps::StructOpsFuncInfo::Error = core::convert::Infallible +pub fn aya_obj::maps::StructOpsFuncInfo::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::maps::StructOpsFuncInfo where U: core::convert::TryFrom +pub type aya_obj::maps::StructOpsFuncInfo::Error = >::Error +pub fn aya_obj::maps::StructOpsFuncInfo::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya_obj::maps::StructOpsFuncInfo where T: core::clone::Clone +pub type aya_obj::maps::StructOpsFuncInfo::Owned = T +pub fn aya_obj::maps::StructOpsFuncInfo::clone_into(&self, target: &mut T) +pub fn aya_obj::maps::StructOpsFuncInfo::to_owned(&self) -> T +impl core::any::Any for aya_obj::maps::StructOpsFuncInfo where T: 'static + ?core::marker::Sized +pub fn aya_obj::maps::StructOpsFuncInfo::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::maps::StructOpsFuncInfo where T: ?core::marker::Sized +pub fn aya_obj::maps::StructOpsFuncInfo::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::maps::StructOpsFuncInfo where T: ?core::marker::Sized +pub fn aya_obj::maps::StructOpsFuncInfo::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_obj::maps::StructOpsFuncInfo where T: core::clone::Clone +pub unsafe fn aya_obj::maps::StructOpsFuncInfo::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_obj::maps::StructOpsFuncInfo +pub fn aya_obj::maps::StructOpsFuncInfo::from(t: T) -> T +pub struct aya_obj::maps::StructOpsMap +pub aya_obj::maps::StructOpsMap::btf_type_id: u32 +pub aya_obj::maps::StructOpsMap::data: alloc::vec::Vec +pub aya_obj::maps::StructOpsMap::data_offset: u32 +pub aya_obj::maps::StructOpsMap::func_info: alloc::vec::Vec +pub aya_obj::maps::StructOpsMap::is_link: bool +pub aya_obj::maps::StructOpsMap::section_index: usize +pub aya_obj::maps::StructOpsMap::symbol_index: usize +pub aya_obj::maps::StructOpsMap::type_name: alloc::string::String +impl core::clone::Clone for aya_obj::maps::StructOpsMap +pub fn aya_obj::maps::StructOpsMap::clone(&self) -> aya_obj::maps::StructOpsMap +impl core::fmt::Debug for aya_obj::maps::StructOpsMap +pub fn aya_obj::maps::StructOpsMap::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya_obj::maps::StructOpsMap +impl core::marker::Send for aya_obj::maps::StructOpsMap +impl core::marker::Sync for aya_obj::maps::StructOpsMap +impl core::marker::Unpin for aya_obj::maps::StructOpsMap +impl core::panic::unwind_safe::RefUnwindSafe for aya_obj::maps::StructOpsMap +impl core::panic::unwind_safe::UnwindSafe for aya_obj::maps::StructOpsMap +impl core::convert::Into for aya_obj::maps::StructOpsMap where U: core::convert::From +pub fn aya_obj::maps::StructOpsMap::into(self) -> U +impl core::convert::TryFrom for aya_obj::maps::StructOpsMap where U: core::convert::Into +pub type aya_obj::maps::StructOpsMap::Error = core::convert::Infallible +pub fn aya_obj::maps::StructOpsMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya_obj::maps::StructOpsMap where U: core::convert::TryFrom +pub type aya_obj::maps::StructOpsMap::Error = >::Error +pub fn aya_obj::maps::StructOpsMap::try_into(self) -> core::result::Result>::Error> +impl alloc::borrow::ToOwned for aya_obj::maps::StructOpsMap where T: core::clone::Clone +pub type aya_obj::maps::StructOpsMap::Owned = T +pub fn aya_obj::maps::StructOpsMap::clone_into(&self, target: &mut T) +pub fn aya_obj::maps::StructOpsMap::to_owned(&self) -> T +impl core::any::Any for aya_obj::maps::StructOpsMap where T: 'static + ?core::marker::Sized +pub fn aya_obj::maps::StructOpsMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya_obj::maps::StructOpsMap where T: ?core::marker::Sized +pub fn aya_obj::maps::StructOpsMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya_obj::maps::StructOpsMap where T: ?core::marker::Sized +pub fn aya_obj::maps::StructOpsMap::borrow_mut(&mut self) -> &mut T +impl core::clone::CloneToUninit for aya_obj::maps::StructOpsMap where T: core::clone::Clone +pub unsafe fn aya_obj::maps::StructOpsMap::clone_to_uninit(&self, dest: *mut u8) +impl core::convert::From for aya_obj::maps::StructOpsMap +pub fn aya_obj::maps::StructOpsMap::from(t: T) -> T #[repr(C)] pub struct aya_obj::maps::bpf_map_def pub aya_obj::maps::bpf_map_def::id: u32 pub aya_obj::maps::bpf_map_def::key_size: u32 @@ -8493,10 +8575,13 @@ pub aya_obj::obj::EbpfSectionKind::Btf pub aya_obj::obj::EbpfSectionKind::BtfExt pub aya_obj::obj::EbpfSectionKind::BtfMaps pub aya_obj::obj::EbpfSectionKind::Data +pub aya_obj::obj::EbpfSectionKind::Ksyms pub aya_obj::obj::EbpfSectionKind::License pub aya_obj::obj::EbpfSectionKind::Maps pub aya_obj::obj::EbpfSectionKind::Program pub aya_obj::obj::EbpfSectionKind::Rodata +pub aya_obj::obj::EbpfSectionKind::StructOps +pub aya_obj::obj::EbpfSectionKind::StructOpsLink pub aya_obj::obj::EbpfSectionKind::Text pub aya_obj::obj::EbpfSectionKind::Undefined pub aya_obj::obj::EbpfSectionKind::Version @@ -8645,6 +8730,10 @@ pub aya_obj::obj::ProgramSection::SkSkbStreamParser pub aya_obj::obj::ProgramSection::SkSkbStreamVerdict pub aya_obj::obj::ProgramSection::SockOps pub aya_obj::obj::ProgramSection::SocketFilter +pub aya_obj::obj::ProgramSection::StructOps +pub aya_obj::obj::ProgramSection::StructOps::member_name: alloc::string::String +pub aya_obj::obj::ProgramSection::StructOps::sleepable: bool +pub aya_obj::obj::ProgramSection::Syscall pub aya_obj::obj::ProgramSection::TracePoint pub aya_obj::obj::ProgramSection::UProbe pub aya_obj::obj::ProgramSection::UProbe::sleepable: bool @@ -8809,7 +8898,7 @@ pub fn aya_obj::Object::sanitize_functions(&mut self, features: &aya_obj::Featur impl aya_obj::Object pub fn aya_obj::Object::relocate_btf(&mut self, target_btf: &aya_obj::btf::Btf) -> core::result::Result<(), aya_obj::btf::BtfRelocationError> impl aya_obj::Object -pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> +pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet, kernel_btf: core::option::Option<&aya_obj::btf::Btf>) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> pub fn aya_obj::Object::relocate_maps<'a, I: core::iter::traits::iterator::Iterator>(&mut self, maps: I, text_sections: &std::collections::hash::set::HashSet) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> impl core::clone::Clone for aya_obj::Object pub fn aya_obj::Object::clone(&self) -> aya_obj::Object @@ -9307,10 +9396,13 @@ pub aya_obj::EbpfSectionKind::Btf pub aya_obj::EbpfSectionKind::BtfExt pub aya_obj::EbpfSectionKind::BtfMaps pub aya_obj::EbpfSectionKind::Data +pub aya_obj::EbpfSectionKind::Ksyms pub aya_obj::EbpfSectionKind::License pub aya_obj::EbpfSectionKind::Maps pub aya_obj::EbpfSectionKind::Program pub aya_obj::EbpfSectionKind::Rodata +pub aya_obj::EbpfSectionKind::StructOps +pub aya_obj::EbpfSectionKind::StructOpsLink pub aya_obj::EbpfSectionKind::Text pub aya_obj::EbpfSectionKind::Undefined pub aya_obj::EbpfSectionKind::Version @@ -9354,7 +9446,9 @@ pub fn aya_obj::EbpfSectionKind::from(t: T) -> T pub enum aya_obj::Map pub aya_obj::Map::Btf(aya_obj::maps::BtfMap) pub aya_obj::Map::Legacy(aya_obj::maps::LegacyMap) +pub aya_obj::Map::StructOps(aya_obj::maps::StructOpsMap) impl aya_obj::maps::Map +pub fn aya_obj::maps::Map::btf_value_type_id(&self) -> core::option::Option pub fn aya_obj::maps::Map::data(&self) -> &[u8] pub fn aya_obj::maps::Map::data_mut(&mut self) -> &mut alloc::vec::Vec pub fn aya_obj::maps::Map::key_size(&self) -> u32 @@ -9508,6 +9602,10 @@ pub aya_obj::ProgramSection::SkSkbStreamParser pub aya_obj::ProgramSection::SkSkbStreamVerdict pub aya_obj::ProgramSection::SockOps pub aya_obj::ProgramSection::SocketFilter +pub aya_obj::ProgramSection::StructOps +pub aya_obj::ProgramSection::StructOps::member_name: alloc::string::String +pub aya_obj::ProgramSection::StructOps::sleepable: bool +pub aya_obj::ProgramSection::Syscall pub aya_obj::ProgramSection::TracePoint pub aya_obj::ProgramSection::UProbe pub aya_obj::ProgramSection::UProbe::sleepable: bool @@ -9672,7 +9770,7 @@ pub fn aya_obj::Object::sanitize_functions(&mut self, features: &aya_obj::Featur impl aya_obj::Object pub fn aya_obj::Object::relocate_btf(&mut self, target_btf: &aya_obj::btf::Btf) -> core::result::Result<(), aya_obj::btf::BtfRelocationError> impl aya_obj::Object -pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> +pub fn aya_obj::Object::relocate_calls(&mut self, text_sections: &std::collections::hash::set::HashSet, kernel_btf: core::option::Option<&aya_obj::btf::Btf>) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> pub fn aya_obj::Object::relocate_maps<'a, I: core::iter::traits::iterator::Iterator>(&mut self, maps: I, text_sections: &std::collections::hash::set::HashSet) -> core::result::Result<(), aya_obj::relocation::EbpfRelocationError> impl core::clone::Clone for aya_obj::Object pub fn aya_obj::Object::clone(&self) -> aya_obj::Object diff --git a/xtask/public-api/aya.txt b/xtask/public-api/aya.txt index 0ddcea1be..2b2283f18 100644 --- a/xtask/public-api/aya.txt +++ b/xtask/public-api/aya.txt @@ -904,6 +904,57 @@ impl core::borrow::BorrowMut for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::from(t: T) -> T +pub mod aya::maps::struct_ops +pub struct aya::maps::struct_ops::StructOpsMap +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::attach(&self) -> core::result::Result +pub fn aya::maps::struct_ops::StructOpsMap::register(&self) -> core::result::Result<(), aya::maps::MapError> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::fd(&self) -> &aya::maps::MapFd +pub fn aya::maps::struct_ops::StructOpsMap::func_info(&self) -> core::option::Option<&[aya_obj::maps::StructOpsFuncInfo]> +pub fn aya::maps::struct_ops::StructOpsMap::is_link(&self) -> bool +pub fn aya::maps::struct_ops::StructOpsMap::type_name(&self) -> core::option::Option<&str> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::set_field_i32(&mut self, offset: u32, value: i32) -> core::result::Result<(), aya::maps::MapError> +pub fn aya::maps::struct_ops::StructOpsMap::set_field_u32(&mut self, offset: u32, value: u32) -> core::result::Result<(), aya::maps::MapError> +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::MapData) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::fmt::Debug for aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya::maps::struct_ops::StructOpsMap where T: core::marker::Freeze +impl core::marker::Send for aya::maps::struct_ops::StructOpsMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::struct_ops::StructOpsMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::struct_ops::StructOpsMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::struct_ops::StructOpsMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::struct_ops::StructOpsMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::struct_ops::StructOpsMap where U: core::convert::From +pub fn aya::maps::struct_ops::StructOpsMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap where U: core::convert::Into +pub type aya::maps::struct_ops::StructOpsMap::Error = core::convert::Infallible +pub fn aya::maps::struct_ops::StructOpsMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::struct_ops::StructOpsMap where U: core::convert::TryFrom +pub type aya::maps::struct_ops::StructOpsMap::Error = >::Error +pub fn aya::maps::struct_ops::StructOpsMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::struct_ops::StructOpsMap where T: 'static + ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::struct_ops::StructOpsMap where T: ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::struct_ops::StructOpsMap where T: ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::from(t: T) -> T pub mod aya::maps::xdp pub enum aya::maps::xdp::XdpMapError pub aya::maps::xdp::XdpMapError::ChainedProgramNotSupported @@ -1121,6 +1172,7 @@ pub aya::maps::Map::SockHash(aya::maps::MapData) pub aya::maps::Map::SockMap(aya::maps::MapData) pub aya::maps::Map::Stack(aya::maps::MapData) pub aya::maps::Map::StackTraceMap(aya::maps::MapData) +pub aya::maps::Map::StructOps(aya::maps::MapData) pub aya::maps::Map::Unsupported(aya::maps::MapData) pub aya::maps::Map::XskMap(aya::maps::MapData) impl aya::maps::Map @@ -1153,6 +1205,9 @@ pub fn aya::maps::ring_buf::RingBuf::try_from(map: aya::maps impl core::convert::TryFrom for aya::maps::stack_trace::StackTraceMap pub type aya::maps::stack_trace::StackTraceMap::Error = aya::maps::MapError pub fn aya::maps::stack_trace::StackTraceMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::Map) -> core::result::Result impl core::fmt::Debug for aya::maps::Map pub fn aya::maps::Map::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl<'a, K: aya::Pod, V: aya::Pod> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::hash_map::HashMap<&'a aya::maps::MapData, K, V> @@ -1242,6 +1297,9 @@ pub fn aya::maps::ring_buf::RingBuf<&'a aya::maps::MapData>::try_from(map: &'a a impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData> pub type aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::stack_trace::StackTraceMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::CpuMap<&'a mut aya::maps::MapData> pub type aya::maps::CpuMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::CpuMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result @@ -1269,6 +1327,9 @@ pub fn aya::maps::ring_buf::RingBuf<&'a mut aya::maps::MapData>::try_from(map: & impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::stack_trace::StackTraceMap<&'a mut aya::maps::MapData> pub type aya::maps::stack_trace::StackTraceMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError pub fn aya::maps::stack_trace::StackTraceMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result impl core::convert::TryFrom for aya::maps::hash_map::HashMap pub type aya::maps::hash_map::HashMap::Error = aya::maps::MapError pub fn aya::maps::hash_map::HashMap::try_from(map: aya::maps::Map) -> core::result::Result @@ -1771,6 +1832,9 @@ pub fn aya::maps::MapData::from_id(id: u32) -> core::result::Result>(path: P) -> core::result::Result pub fn aya::maps::MapData::info(&self) -> core::result::Result pub fn aya::maps::MapData::pin>(&self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::MapData) -> core::result::Result impl core::fmt::Debug for aya::maps::MapData pub fn aya::maps::MapData::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::marker::Freeze for aya::maps::MapData @@ -2411,6 +2475,56 @@ impl core::borrow::BorrowMut for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::maps::stack_trace::StackTraceMap pub fn aya::maps::stack_trace::StackTraceMap::from(t: T) -> T +pub struct aya::maps::StructOpsMap +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::attach(&self) -> core::result::Result +pub fn aya::maps::struct_ops::StructOpsMap::register(&self) -> core::result::Result<(), aya::maps::MapError> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::fd(&self) -> &aya::maps::MapFd +pub fn aya::maps::struct_ops::StructOpsMap::func_info(&self) -> core::option::Option<&[aya_obj::maps::StructOpsFuncInfo]> +pub fn aya::maps::struct_ops::StructOpsMap::is_link(&self) -> bool +pub fn aya::maps::struct_ops::StructOpsMap::type_name(&self) -> core::option::Option<&str> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::pin>(self, path: P) -> core::result::Result<(), aya::pin::PinError> +impl> aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::set_field_i32(&mut self, offset: u32, value: i32) -> core::result::Result<(), aya::maps::MapError> +pub fn aya::maps::struct_ops::StructOpsMap::set_field_u32(&mut self, offset: u32, value: u32) -> core::result::Result<(), aya::maps::MapError> +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::Map) -> core::result::Result +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap +pub type aya::maps::struct_ops::StructOpsMap::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap::try_from(map: aya::maps::MapData) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a aya::maps::MapData>::try_from(map: &'a aya::maps::Map) -> core::result::Result +impl<'a> core::convert::TryFrom<&'a mut aya::maps::Map> for aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData> +pub type aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::Error = aya::maps::MapError +pub fn aya::maps::struct_ops::StructOpsMap<&'a mut aya::maps::MapData>::try_from(map: &'a mut aya::maps::Map) -> core::result::Result +impl core::fmt::Debug for aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::marker::Freeze for aya::maps::struct_ops::StructOpsMap where T: core::marker::Freeze +impl core::marker::Send for aya::maps::struct_ops::StructOpsMap where T: core::marker::Send +impl core::marker::Sync for aya::maps::struct_ops::StructOpsMap where T: core::marker::Sync +impl core::marker::Unpin for aya::maps::struct_ops::StructOpsMap where T: core::marker::Unpin +impl core::panic::unwind_safe::RefUnwindSafe for aya::maps::struct_ops::StructOpsMap where T: core::panic::unwind_safe::RefUnwindSafe +impl core::panic::unwind_safe::UnwindSafe for aya::maps::struct_ops::StructOpsMap where T: core::panic::unwind_safe::UnwindSafe +impl core::convert::Into for aya::maps::struct_ops::StructOpsMap where U: core::convert::From +pub fn aya::maps::struct_ops::StructOpsMap::into(self) -> U +impl core::convert::TryFrom for aya::maps::struct_ops::StructOpsMap where U: core::convert::Into +pub type aya::maps::struct_ops::StructOpsMap::Error = core::convert::Infallible +pub fn aya::maps::struct_ops::StructOpsMap::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::maps::struct_ops::StructOpsMap where U: core::convert::TryFrom +pub type aya::maps::struct_ops::StructOpsMap::Error = >::Error +pub fn aya::maps::struct_ops::StructOpsMap::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::maps::struct_ops::StructOpsMap where T: 'static + ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::maps::struct_ops::StructOpsMap where T: ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::maps::struct_ops::StructOpsMap where T: ?core::marker::Sized +pub fn aya::maps::struct_ops::StructOpsMap::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::maps::struct_ops::StructOpsMap +pub fn aya::maps::struct_ops::StructOpsMap::from(t: T) -> T pub struct aya::maps::XskMap impl> aya::maps::XskMap pub fn aya::maps::XskMap::len(&self) -> u32 @@ -4396,6 +4510,8 @@ impl core::convert::From for aya::programs::raw_tr pub fn aya::programs::raw_trace_point::RawTracePointLink::from(b: aya::programs::links::FdLink) -> aya::programs::raw_trace_point::RawTracePointLink impl core::convert::From for aya::programs::sk_lookup::SkLookupLink pub fn aya::programs::sk_lookup::SkLookupLink::from(b: aya::programs::links::FdLink) -> aya::programs::sk_lookup::SkLookupLink +impl core::convert::From for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::from(b: aya::programs::links::FdLink) -> aya::programs::struct_ops::StructOpsLink impl core::convert::From for aya::programs::tp_btf::BtfTracePointLink pub fn aya::programs::tp_btf::BtfTracePointLink::from(b: aya::programs::links::FdLink) -> aya::programs::tp_btf::BtfTracePointLink impl core::convert::From for aya::programs::links::FdLink @@ -4408,6 +4524,8 @@ impl core::convert::From for pub fn aya::programs::links::FdLink::from(w: aya::programs::raw_trace_point::RawTracePointLink) -> aya::programs::links::FdLink impl core::convert::From for aya::programs::links::FdLink pub fn aya::programs::links::FdLink::from(w: aya::programs::sk_lookup::SkLookupLink) -> aya::programs::links::FdLink +impl core::convert::From for aya::programs::links::FdLink +pub fn aya::programs::links::FdLink::from(w: aya::programs::struct_ops::StructOpsLink) -> aya::programs::links::FdLink impl core::convert::From for aya::programs::links::FdLink pub fn aya::programs::links::FdLink::from(w: aya::programs::tp_btf::BtfTracePointLink) -> aya::programs::links::FdLink impl core::convert::TryFrom for aya::programs::links::FdLink @@ -4809,6 +4927,14 @@ impl aya::programs::links::Link for aya::programs::socket_filter::SocketFilterLi pub type aya::programs::socket_filter::SocketFilterLink::Id = aya::programs::socket_filter::SocketFilterLinkId pub fn aya::programs::socket_filter::SocketFilterLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::socket_filter::SocketFilterLink::id(&self) -> Self::Id +impl aya::programs::links::Link for aya::programs::struct_ops::StructOpsLink +pub type aya::programs::struct_ops::StructOpsLink::Id = aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOpsLink::id(&self) -> Self::Id +impl aya::programs::links::Link for aya::programs::syscall::SyscallLink +pub type aya::programs::syscall::SyscallLink::Id = aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::syscall::SyscallLink::id(&self) -> Self::Id impl aya::programs::links::Link for aya::programs::tc::SchedClassifierLink pub type aya::programs::tc::SchedClassifierLink::Id = aya::programs::tc::SchedClassifierLinkId pub fn aya::programs::tc::SchedClassifierLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> @@ -6691,6 +6817,253 @@ impl core::borrow::BorrowMut for aya::programs::socket_filter::SocketFilte pub fn aya::programs::socket_filter::SocketFilterLinkId::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::socket_filter::SocketFilterLinkId pub fn aya::programs::socket_filter::SocketFilterLinkId::from(t: T) -> T +pub mod aya::programs::struct_ops +pub struct aya::programs::struct_ops::StructOps +impl aya::programs::struct_ops::StructOps +pub const aya::programs::struct_ops::StructOps::PROGRAM_TYPE: aya::programs::ProgramType +pub fn aya::programs::struct_ops::StructOps::load(&mut self, struct_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOps::member_name(&self) -> &str +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::from_pin>(path: P) -> core::result::Result +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::info(&self) -> core::result::Result +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> +pub fn aya::programs::struct_ops::StructOps::unpin(&mut self) -> core::result::Result<(), std::io::error::Error> +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl core::fmt::Debug for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::ops::drop::Drop for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::struct_ops::StructOps +pub type &'a aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a aya::programs::struct_ops::StructOps::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::struct_ops::StructOps +pub type &'a mut aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::struct_ops::StructOps::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl core::marker::Freeze for aya::programs::struct_ops::StructOps +impl core::marker::Send for aya::programs::struct_ops::StructOps +impl core::marker::Sync for aya::programs::struct_ops::StructOps +impl core::marker::Unpin for aya::programs::struct_ops::StructOps +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::struct_ops::StructOps +impl core::panic::unwind_safe::UnwindSafe for aya::programs::struct_ops::StructOps +impl core::convert::Into for aya::programs::struct_ops::StructOps where U: core::convert::From +pub fn aya::programs::struct_ops::StructOps::into(self) -> U +impl core::convert::TryFrom for aya::programs::struct_ops::StructOps where U: core::convert::Into +pub type aya::programs::struct_ops::StructOps::Error = core::convert::Infallible +pub fn aya::programs::struct_ops::StructOps::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::struct_ops::StructOps where U: core::convert::TryFrom +pub type aya::programs::struct_ops::StructOps::Error = >::Error +pub fn aya::programs::struct_ops::StructOps::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::struct_ops::StructOps where T: 'static + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::struct_ops::StructOps where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::struct_ops::StructOps where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::from(t: T) -> T +pub struct aya::programs::struct_ops::StructOpsLink(_) +impl aya::programs::links::Link for aya::programs::struct_ops::StructOpsLink +pub type aya::programs::struct_ops::StructOpsLink::Id = aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOpsLink::id(&self) -> Self::Id +impl core::cmp::Eq for aya::programs::struct_ops::StructOpsLink +impl core::cmp::PartialEq for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::eq(&self, other: &Self) -> bool +impl core::convert::From for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::from(b: aya::programs::links::FdLink) -> aya::programs::struct_ops::StructOpsLink +impl core::convert::From for aya::programs::links::FdLink +pub fn aya::programs::links::FdLink::from(w: aya::programs::struct_ops::StructOpsLink) -> aya::programs::links::FdLink +impl core::fmt::Debug for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::hash(&self, state: &mut H) +impl core::ops::drop::Drop for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::drop(&mut self) +impl equivalent::Equivalent for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::equivalent(&self, key: &aya::programs::struct_ops::StructOpsLink) -> bool +impl core::marker::Freeze for aya::programs::struct_ops::StructOpsLink +impl core::marker::Send for aya::programs::struct_ops::StructOpsLink +impl core::marker::Sync for aya::programs::struct_ops::StructOpsLink +impl core::marker::Unpin for aya::programs::struct_ops::StructOpsLink +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::struct_ops::StructOpsLink +impl core::panic::unwind_safe::UnwindSafe for aya::programs::struct_ops::StructOpsLink +impl equivalent::Equivalent for aya::programs::struct_ops::StructOpsLink where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLink::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::struct_ops::StructOpsLink where U: core::convert::From +pub fn aya::programs::struct_ops::StructOpsLink::into(self) -> U +impl core::convert::TryFrom for aya::programs::struct_ops::StructOpsLink where U: core::convert::Into +pub type aya::programs::struct_ops::StructOpsLink::Error = core::convert::Infallible +pub fn aya::programs::struct_ops::StructOpsLink::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::struct_ops::StructOpsLink where U: core::convert::TryFrom +pub type aya::programs::struct_ops::StructOpsLink::Error = >::Error +pub fn aya::programs::struct_ops::StructOpsLink::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::struct_ops::StructOpsLink where T: 'static + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLink::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::struct_ops::StructOpsLink where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLink::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::struct_ops::StructOpsLink where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLink::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::struct_ops::StructOpsLink +pub fn aya::programs::struct_ops::StructOpsLink::from(t: T) -> T +pub struct aya::programs::struct_ops::StructOpsLinkId(_) +impl core::cmp::Eq for aya::programs::struct_ops::StructOpsLinkId +impl core::cmp::PartialEq for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::eq(&self, other: &aya::programs::struct_ops::StructOpsLinkId) -> bool +impl core::fmt::Debug for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::StructuralPartialEq for aya::programs::struct_ops::StructOpsLinkId +impl equivalent::Equivalent for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::equivalent(&self, key: &aya::programs::struct_ops::StructOpsLink) -> bool +impl core::marker::Freeze for aya::programs::struct_ops::StructOpsLinkId +impl core::marker::Send for aya::programs::struct_ops::StructOpsLinkId +impl core::marker::Sync for aya::programs::struct_ops::StructOpsLinkId +impl core::marker::Unpin for aya::programs::struct_ops::StructOpsLinkId +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::struct_ops::StructOpsLinkId +impl core::panic::unwind_safe::UnwindSafe for aya::programs::struct_ops::StructOpsLinkId +impl equivalent::Equivalent for aya::programs::struct_ops::StructOpsLinkId where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLinkId::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::struct_ops::StructOpsLinkId where U: core::convert::From +pub fn aya::programs::struct_ops::StructOpsLinkId::into(self) -> U +impl core::convert::TryFrom for aya::programs::struct_ops::StructOpsLinkId where U: core::convert::Into +pub type aya::programs::struct_ops::StructOpsLinkId::Error = core::convert::Infallible +pub fn aya::programs::struct_ops::StructOpsLinkId::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::struct_ops::StructOpsLinkId where U: core::convert::TryFrom +pub type aya::programs::struct_ops::StructOpsLinkId::Error = >::Error +pub fn aya::programs::struct_ops::StructOpsLinkId::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::struct_ops::StructOpsLinkId where T: 'static + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLinkId::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::struct_ops::StructOpsLinkId where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLinkId::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::struct_ops::StructOpsLinkId where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOpsLinkId::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLinkId::from(t: T) -> T +pub mod aya::programs::syscall +pub struct aya::programs::syscall::Syscall +impl aya::programs::syscall::Syscall +pub const aya::programs::syscall::Syscall::PROGRAM_TYPE: aya::programs::ProgramType +pub fn aya::programs::syscall::Syscall::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::info(&self) -> core::result::Result +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> +pub fn aya::programs::syscall::Syscall::unpin(&mut self) -> core::result::Result<(), std::io::error::Error> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl core::fmt::Debug for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::ops::drop::Drop for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::syscall::Syscall +pub type &'a aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a aya::programs::syscall::Syscall::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::syscall::Syscall, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::syscall::Syscall +pub type &'a mut aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::syscall::Syscall::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::syscall::Syscall, aya::programs::ProgramError> +impl core::marker::Freeze for aya::programs::syscall::Syscall +impl core::marker::Send for aya::programs::syscall::Syscall +impl core::marker::Sync for aya::programs::syscall::Syscall +impl core::marker::Unpin for aya::programs::syscall::Syscall +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::syscall::Syscall +impl core::panic::unwind_safe::UnwindSafe for aya::programs::syscall::Syscall +impl core::convert::Into for aya::programs::syscall::Syscall where U: core::convert::From +pub fn aya::programs::syscall::Syscall::into(self) -> U +impl core::convert::TryFrom for aya::programs::syscall::Syscall where U: core::convert::Into +pub type aya::programs::syscall::Syscall::Error = core::convert::Infallible +pub fn aya::programs::syscall::Syscall::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::syscall::Syscall where U: core::convert::TryFrom +pub type aya::programs::syscall::Syscall::Error = >::Error +pub fn aya::programs::syscall::Syscall::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::syscall::Syscall where T: 'static + ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::syscall::Syscall where T: ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::syscall::Syscall where T: ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::from(t: T) -> T +pub struct aya::programs::syscall::SyscallLink +impl aya::programs::links::Link for aya::programs::syscall::SyscallLink +pub type aya::programs::syscall::SyscallLink::Id = aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::syscall::SyscallLink::id(&self) -> Self::Id +impl core::cmp::Eq for aya::programs::syscall::SyscallLink +impl core::cmp::PartialEq for aya::programs::syscall::SyscallLink +pub fn aya::programs::syscall::SyscallLink::eq(&self, other: &Self) -> bool +impl core::fmt::Debug for aya::programs::syscall::SyscallLink +pub fn aya::programs::syscall::SyscallLink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::syscall::SyscallLink +pub fn aya::programs::syscall::SyscallLink::hash(&self, state: &mut H) +impl equivalent::Equivalent for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::equivalent(&self, key: &aya::programs::syscall::SyscallLink) -> bool +impl core::marker::Freeze for aya::programs::syscall::SyscallLink +impl core::marker::Send for aya::programs::syscall::SyscallLink +impl core::marker::Sync for aya::programs::syscall::SyscallLink +impl core::marker::Unpin for aya::programs::syscall::SyscallLink +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::syscall::SyscallLink +impl core::panic::unwind_safe::UnwindSafe for aya::programs::syscall::SyscallLink +impl equivalent::Equivalent for aya::programs::syscall::SyscallLink where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow + ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLink::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::syscall::SyscallLink where U: core::convert::From +pub fn aya::programs::syscall::SyscallLink::into(self) -> U +impl core::convert::TryFrom for aya::programs::syscall::SyscallLink where U: core::convert::Into +pub type aya::programs::syscall::SyscallLink::Error = core::convert::Infallible +pub fn aya::programs::syscall::SyscallLink::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::syscall::SyscallLink where U: core::convert::TryFrom +pub type aya::programs::syscall::SyscallLink::Error = >::Error +pub fn aya::programs::syscall::SyscallLink::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::syscall::SyscallLink where T: 'static + ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLink::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::syscall::SyscallLink where T: ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLink::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::syscall::SyscallLink where T: ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLink::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::syscall::SyscallLink +pub fn aya::programs::syscall::SyscallLink::from(t: T) -> T +pub struct aya::programs::syscall::SyscallLinkId(_) +impl core::cmp::Eq for aya::programs::syscall::SyscallLinkId +impl core::cmp::PartialEq for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::eq(&self, other: &aya::programs::syscall::SyscallLinkId) -> bool +impl core::fmt::Debug for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::hash::Hash for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::hash<__H: core::hash::Hasher>(&self, state: &mut __H) +impl core::marker::StructuralPartialEq for aya::programs::syscall::SyscallLinkId +impl equivalent::Equivalent for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::equivalent(&self, key: &aya::programs::syscall::SyscallLink) -> bool +impl core::marker::Freeze for aya::programs::syscall::SyscallLinkId +impl core::marker::Send for aya::programs::syscall::SyscallLinkId +impl core::marker::Sync for aya::programs::syscall::SyscallLinkId +impl core::marker::Unpin for aya::programs::syscall::SyscallLinkId +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::syscall::SyscallLinkId +impl core::panic::unwind_safe::UnwindSafe for aya::programs::syscall::SyscallLinkId +impl equivalent::Equivalent for aya::programs::syscall::SyscallLinkId where Q: core::cmp::Eq + ?core::marker::Sized, K: core::borrow::Borrow + ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLinkId::equivalent(&self, key: &K) -> bool +impl core::convert::Into for aya::programs::syscall::SyscallLinkId where U: core::convert::From +pub fn aya::programs::syscall::SyscallLinkId::into(self) -> U +impl core::convert::TryFrom for aya::programs::syscall::SyscallLinkId where U: core::convert::Into +pub type aya::programs::syscall::SyscallLinkId::Error = core::convert::Infallible +pub fn aya::programs::syscall::SyscallLinkId::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::syscall::SyscallLinkId where U: core::convert::TryFrom +pub type aya::programs::syscall::SyscallLinkId::Error = >::Error +pub fn aya::programs::syscall::SyscallLinkId::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::syscall::SyscallLinkId where T: 'static + ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLinkId::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::syscall::SyscallLinkId where T: ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLinkId::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::syscall::SyscallLinkId where T: ?core::marker::Sized +pub fn aya::programs::syscall::SyscallLinkId::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLinkId::from(t: T) -> T pub mod aya::programs::tc pub enum aya::programs::tc::TcAttachOptions pub aya::programs::tc::TcAttachOptions::Netlink(aya::programs::tc::NlOptions) @@ -8101,6 +8474,8 @@ pub aya::programs::Program::SkMsg(aya::programs::sk_msg::SkMsg) pub aya::programs::Program::SkSkb(aya::programs::sk_skb::SkSkb) pub aya::programs::Program::SockOps(aya::programs::sock_ops::SockOps) pub aya::programs::Program::SocketFilter(aya::programs::socket_filter::SocketFilter) +pub aya::programs::Program::StructOps(aya::programs::struct_ops::StructOps) +pub aya::programs::Program::Syscall(aya::programs::syscall::Syscall) pub aya::programs::Program::TracePoint(aya::programs::trace_point::TracePoint) pub aya::programs::Program::UProbe(aya::programs::uprobe::UProbe) pub aya::programs::Program::Xdp(aya::programs::xdp::Xdp) @@ -8178,6 +8553,12 @@ pub fn &'a aya::programs::sock_ops::SockOps::try_from(program: &'a aya::programs impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::socket_filter::SocketFilter pub type &'a aya::programs::socket_filter::SocketFilter::Error = aya::programs::ProgramError pub fn &'a aya::programs::socket_filter::SocketFilter::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::socket_filter::SocketFilter, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::struct_ops::StructOps +pub type &'a aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a aya::programs::struct_ops::StructOps::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::syscall::Syscall +pub type &'a aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a aya::programs::syscall::Syscall::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::syscall::Syscall, aya::programs::ProgramError> impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::tc::SchedClassifier pub type &'a aya::programs::tc::SchedClassifier::Error = aya::programs::ProgramError pub fn &'a aya::programs::tc::SchedClassifier::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::tc::SchedClassifier, aya::programs::ProgramError> @@ -8259,6 +8640,12 @@ pub fn &'a mut aya::programs::sock_ops::SockOps::try_from(program: &'a mut aya:: impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::socket_filter::SocketFilter pub type &'a mut aya::programs::socket_filter::SocketFilter::Error = aya::programs::ProgramError pub fn &'a mut aya::programs::socket_filter::SocketFilter::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::socket_filter::SocketFilter, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::struct_ops::StructOps +pub type &'a mut aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::struct_ops::StructOps::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::syscall::Syscall +pub type &'a mut aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::syscall::Syscall::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::syscall::Syscall, aya::programs::ProgramError> impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::tc::SchedClassifier pub type &'a mut aya::programs::tc::SchedClassifier::Error = aya::programs::ProgramError pub fn &'a mut aya::programs::tc::SchedClassifier::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::tc::SchedClassifier, aya::programs::ProgramError> @@ -10118,6 +10505,99 @@ impl core::borrow::BorrowMut for aya::programs::socket_filter::SocketFilte pub fn aya::programs::socket_filter::SocketFilter::borrow_mut(&mut self) -> &mut T impl core::convert::From for aya::programs::socket_filter::SocketFilter pub fn aya::programs::socket_filter::SocketFilter::from(t: T) -> T +pub struct aya::programs::StructOps +impl aya::programs::struct_ops::StructOps +pub const aya::programs::struct_ops::StructOps::PROGRAM_TYPE: aya::programs::ProgramType +pub fn aya::programs::struct_ops::StructOps::load(&mut self, struct_name: &str, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOps::member_name(&self) -> &str +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::from_pin>(path: P) -> core::result::Result +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::info(&self) -> core::result::Result +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> +pub fn aya::programs::struct_ops::StructOps::unpin(&mut self) -> core::result::Result<(), std::io::error::Error> +impl aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl core::fmt::Debug for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::ops::drop::Drop for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::struct_ops::StructOps +pub type &'a aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a aya::programs::struct_ops::StructOps::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::struct_ops::StructOps +pub type &'a mut aya::programs::struct_ops::StructOps::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::struct_ops::StructOps::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::struct_ops::StructOps, aya::programs::ProgramError> +impl core::marker::Freeze for aya::programs::struct_ops::StructOps +impl core::marker::Send for aya::programs::struct_ops::StructOps +impl core::marker::Sync for aya::programs::struct_ops::StructOps +impl core::marker::Unpin for aya::programs::struct_ops::StructOps +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::struct_ops::StructOps +impl core::panic::unwind_safe::UnwindSafe for aya::programs::struct_ops::StructOps +impl core::convert::Into for aya::programs::struct_ops::StructOps where U: core::convert::From +pub fn aya::programs::struct_ops::StructOps::into(self) -> U +impl core::convert::TryFrom for aya::programs::struct_ops::StructOps where U: core::convert::Into +pub type aya::programs::struct_ops::StructOps::Error = core::convert::Infallible +pub fn aya::programs::struct_ops::StructOps::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::struct_ops::StructOps where U: core::convert::TryFrom +pub type aya::programs::struct_ops::StructOps::Error = >::Error +pub fn aya::programs::struct_ops::StructOps::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::struct_ops::StructOps where T: 'static + ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::struct_ops::StructOps where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::struct_ops::StructOps where T: ?core::marker::Sized +pub fn aya::programs::struct_ops::StructOps::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::struct_ops::StructOps +pub fn aya::programs::struct_ops::StructOps::from(t: T) -> T +pub struct aya::programs::Syscall +impl aya::programs::syscall::Syscall +pub const aya::programs::syscall::Syscall::PROGRAM_TYPE: aya::programs::ProgramType +pub fn aya::programs::syscall::Syscall::load(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::fd(&self) -> core::result::Result<&aya::programs::ProgramFd, aya::programs::ProgramError> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::info(&self) -> core::result::Result +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::pin>(&mut self, path: P) -> core::result::Result<(), aya::pin::PinError> +pub fn aya::programs::syscall::Syscall::unpin(&mut self) -> core::result::Result<(), std::io::error::Error> +impl aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::unload(&mut self) -> core::result::Result<(), aya::programs::ProgramError> +impl core::fmt::Debug for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result +impl core::ops::drop::Drop for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::drop(&mut self) +impl<'a> core::convert::TryFrom<&'a aya::programs::Program> for &'a aya::programs::syscall::Syscall +pub type &'a aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a aya::programs::syscall::Syscall::try_from(program: &'a aya::programs::Program) -> core::result::Result<&'a aya::programs::syscall::Syscall, aya::programs::ProgramError> +impl<'a> core::convert::TryFrom<&'a mut aya::programs::Program> for &'a mut aya::programs::syscall::Syscall +pub type &'a mut aya::programs::syscall::Syscall::Error = aya::programs::ProgramError +pub fn &'a mut aya::programs::syscall::Syscall::try_from(program: &'a mut aya::programs::Program) -> core::result::Result<&'a mut aya::programs::syscall::Syscall, aya::programs::ProgramError> +impl core::marker::Freeze for aya::programs::syscall::Syscall +impl core::marker::Send for aya::programs::syscall::Syscall +impl core::marker::Sync for aya::programs::syscall::Syscall +impl core::marker::Unpin for aya::programs::syscall::Syscall +impl core::panic::unwind_safe::RefUnwindSafe for aya::programs::syscall::Syscall +impl core::panic::unwind_safe::UnwindSafe for aya::programs::syscall::Syscall +impl core::convert::Into for aya::programs::syscall::Syscall where U: core::convert::From +pub fn aya::programs::syscall::Syscall::into(self) -> U +impl core::convert::TryFrom for aya::programs::syscall::Syscall where U: core::convert::Into +pub type aya::programs::syscall::Syscall::Error = core::convert::Infallible +pub fn aya::programs::syscall::Syscall::try_from(value: U) -> core::result::Result>::Error> +impl core::convert::TryInto for aya::programs::syscall::Syscall where U: core::convert::TryFrom +pub type aya::programs::syscall::Syscall::Error = >::Error +pub fn aya::programs::syscall::Syscall::try_into(self) -> core::result::Result>::Error> +impl core::any::Any for aya::programs::syscall::Syscall where T: 'static + ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::type_id(&self) -> core::any::TypeId +impl core::borrow::Borrow for aya::programs::syscall::Syscall where T: ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::borrow(&self) -> &T +impl core::borrow::BorrowMut for aya::programs::syscall::Syscall where T: ?core::marker::Sized +pub fn aya::programs::syscall::Syscall::borrow_mut(&mut self) -> &mut T +impl core::convert::From for aya::programs::syscall::Syscall +pub fn aya::programs::syscall::Syscall::from(t: T) -> T pub struct aya::programs::TracePoint impl aya::programs::trace_point::TracePoint pub const aya::programs::trace_point::TracePoint::PROGRAM_TYPE: aya::programs::ProgramType @@ -10491,6 +10971,14 @@ impl aya::programs::links::Link for aya::programs::socket_filter::SocketFilterLi pub type aya::programs::socket_filter::SocketFilterLink::Id = aya::programs::socket_filter::SocketFilterLinkId pub fn aya::programs::socket_filter::SocketFilterLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> pub fn aya::programs::socket_filter::SocketFilterLink::id(&self) -> Self::Id +impl aya::programs::links::Link for aya::programs::struct_ops::StructOpsLink +pub type aya::programs::struct_ops::StructOpsLink::Id = aya::programs::struct_ops::StructOpsLinkId +pub fn aya::programs::struct_ops::StructOpsLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::struct_ops::StructOpsLink::id(&self) -> Self::Id +impl aya::programs::links::Link for aya::programs::syscall::SyscallLink +pub type aya::programs::syscall::SyscallLink::Id = aya::programs::syscall::SyscallLinkId +pub fn aya::programs::syscall::SyscallLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> +pub fn aya::programs::syscall::SyscallLink::id(&self) -> Self::Id impl aya::programs::links::Link for aya::programs::tc::SchedClassifierLink pub type aya::programs::tc::SchedClassifierLink::Id = aya::programs::tc::SchedClassifierLinkId pub fn aya::programs::tc::SchedClassifierLink::detach(self) -> core::result::Result<(), aya::programs::ProgramError> @@ -10718,6 +11206,7 @@ pub struct aya::Ebpf impl aya::Ebpf pub fn aya::Ebpf::load(data: &[u8]) -> core::result::Result pub fn aya::Ebpf::load_file>(path: P) -> core::result::Result +pub fn aya::Ebpf::load_struct_ops(&mut self, btf: &aya_obj::btf::btf::Btf) -> core::result::Result<(), aya::EbpfError> pub fn aya::Ebpf::map(&self, name: &str) -> core::option::Option<&aya::maps::Map> pub fn aya::Ebpf::map_mut(&mut self, name: &str) -> core::option::Option<&mut aya::maps::Map> pub fn aya::Ebpf::maps(&self) -> impl core::iter::traits::iterator::Iterator